2016-06-15 3 views
1

У нас есть несколько типов ресурсов, и мы хотели бы сделать способ проверить, является ли ресурс здоровым или нет. Учитывая, что тип ресурсов очень неоднороден мы не хотим использовать стандартные подклассы, и мы решили использовать класс типов:Scala typeclass pattern and covariance

trait CanHealthCheck[T] { 
    def isHealthy(t: T): Boolean 
} 

У нас также есть служебный метод, чтобы иметь возможность проверить, если данный ресурс жив/здоровый или нет

object LivenessChecker { 
    def isAlive[T](t: T)(implicit canHealthCheck: CanHealthCheck[T]): Boolean = { 
    canHealthCheck.isHealthy(t) 
    } 
} 

У нас есть слой репозитория для доступа к данным. Мы хотели бы выразить мысль, что данное абстрактное хранилище должно быть «здоровье проверяемым», но оставить детали реализации для подклассов реализации признака:

trait UserRepository { 
    def findSomeUser(): User = ??? 

    implicit def isHealthCheckable: CanHealthCheck[UserRepository] 
} 

Проблема возникает, когда мы хотим создать подкласс UserRepository с конкретным при условии, что CanHealthCheck не является covariant по типу T.

class DbUserRepository extends UserRepository { 
    def ping: Boolean = ??? 

    override implicit val isHealthCheckable: CanHealthCheck[UserRepository] = 
    new CanHealthCheck[DbUserRepository] { 
     def isHealthy(db: DbUserRepository) = db.ping 
    } 
} 

И это пример некоторой фиктивной функции, которая действует на абстрактном хранилище при попытке проверить, если хранилище жив:

def someDummyFunction(userRepository: UserRepository) = { 
    if(LivenessChecker.isAlive(userRepository)) // This won't compile 
    userRepository.findSomeUser() 
} 

Идея в том, что наше приложение использует UserRepository а не реализации, и, следовательно, мы не можем проверить, жив ли репозиторий или нет. Как мы можем продолжать использовать слой абстракции репозитория и иметь возможность проверить, жив ли данный (абстрактный) репозиторий? Является ли шаблон typeclass правильным шаблоном для использования здесь?

ответ

0

Используйте «ограничения типа».

Я не мог проверить это, но, чтобы получить код для компиляции, вы могли бы сделать что-то вроде:

class DbUserRepository[U <: UserRepository] extends UserRepository { 
    def ping: Boolean = ??? 

    implicit val isHealthCheckable: CanHealthCheck[U] = 
    new CanHealthCheck[U] { 
     def isHealthy(db: U) = db.ping 
    } 
} 
+0

Не понимаю, как это решить проблему.Нам нужен «CanHealthCheck [UserRepository]», тогда как в вашем примере мы получаем «CanHealthCheck [U]» с 'U <: UserRepository'. Учитывая, что CanHealthCheck не является ** ковариантным ** на T, это не сработает –

+0

Прошу прощения, я пропустил «CanHealthCheck не является ковариантным по типу T» в вашем вопросе. Я оставлю ответ в качестве ссылки на то, как получить код для компиляции ... пока: - \ –

1

Существует что-то немного тусклый с isHealthCheckable внутри UserRespository. Метод isHealthy, при вызове, имел бы два экземпляра UserRepository: конечно, тот, который передавался как аргумент t, но также и UserRepository.this прилагаемого экземпляра.

Это признак чего-то неладного. Либо метод должен быть написан где-то в другом месте, так и не должен содержать его, или он не должен принимать аргумент

Этот второй вариант согласуется с использованием ObjectRepository объектно-ориентированного способа подтипирования. Кроме того, это согласуется с вашей идеей, что каждый UserRepository должен быть доступен для проверки. Просто сделайте

trait UserRepository { 
    ... 
    def isHealty: Boolean 
} 

Это прекрасно назвать, что непосредственно, userDirectory.isHealthy. Но вы можете также легко реализовать класс типа:

object UserRepository { 
    implicit val canHealthCheck = new CanHealthCheck[UserRepository] { 
    def isHealthy(repository: UserRepository) = repository.IsHealthy 
    } 
} 

также отметить, что не было ясно, все как неявный метод экземпляра пришел бы в неявной области. С сопутствующим объектом он отлично работает.

+0

Я согласен, что это, вероятно, признак чего-то неладно. Вопрос в том, можно ли использовать шаблон typeclass и иметь клиентов, зависящих только от признака, или нам нужно использовать классический подход, ориентированный на OO. BTW Я обновил вопрос, чтобы понять, как потенциальный клиент будет использовать абстрактный userRepository –