如何使用共享代码对 class 的更有限版本进行建模?
How to model a more limited version of class with shared code?
我需要创建一个新的 class。它的一些功能已经在另一个 class 中,从领域的角度来看,继承它是有意义的。问题是有一个方法必须在它的参数类型上有更多的限制,因为 LSP (Liskov substitution principle) 你不能覆盖它。
到目前为止有代码,我可能会更改,
为了更好的解释让我举一个简单的例子:
我有 AnimalShelter
,我需要实施 DogShelter
。
class AnimalShelter {
func usefulMethod(...) {}
func take(x: Animal) {}
}
class DogShelter {
var dogMedianCuteness: String = "normal (= very cute)"
func usefulMethod(...) {}
func take(x: Dog) {}
}
解决方案 1:子class
如果DogShelter
是AnimalShelter
的子class那么它将免费得到usefulMethod(...)
,但是继承的方法take(x: Animal)
不能覆盖并污染了 DogShelter 的 API,应该什么都不做或抛出错误。
class AnimalShelter {
func usefulMethod(...) {}
func take(x: Animal) {}
}
class DogShelter: AnimalShelter {
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: Dog) {}
}
方案二:协议+协议扩展
如果AnimalShelter
和DogShelter
实现一个协议,从域的角度来看并不精确,但是共享代码usefulMethod(...)
可以在协议中实现 extension
.
protocol UsefulThing {
func usefulMethod(...)
}
extension UsefulThing {
func usefulMethod(...) { ... }
}
class AnimalShelter: UsefulThing {
func take(x: Animal) {}
}
class DogShelter: UsefulThing {
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: Dog) {}
}
解决方案 3:泛化,创建另一个超级class
问题出在 take(x: T)
方法上,它在 DogShelter
中更为专业。将它从 AnimalShelter 中移除将允许继承而不会出现问题,但是到目前为止使用 AnimalShelter 的所有内容都必须由一个新的 subclass AnyAnimalShelter: AnimalShelter
替换,它有问题 take(x: Animal) {}
class AnimalShelter {
usefulMethod(...) {}
}
class AnyAnimalShelter: AnimalShelter {
take(x: Animal) {}
}
class DogShelter: AnimalShelter {
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: Dog) {}
}
解决方案 4:组合
从域的角度来看继承确实有意义,因此老板认为最好保留。
所以我从 AnimalShelter
获得了代码并允许更改它,尽管它会引起人们的注意,为什么我多年来会完美地更改 运行 的代码。我需要一个关于 take(x: Animal)
方法在 AnimalShelter
中有缺陷的抽象原因。不仅要有充分的理由反对它,而且还要在将来避免它 classes.
如果我无法更改 使用 AnimalShelter
或 AnimalShelter
本身的代码,那将是一个真正的问题。
问题
someone/I 应该如何建模?
Sweeper 建议的解决方案。
class Animal {}
class Dog: Animal {}
当没有任何存储属性时,使用泛型 + class 带有类型约束(where 子句)的扩展。
class AnimalShelter<T: Animal> {
func usefulMethod(...) {}
func take(x: T) {}
}
否则使用解决方案 2:协议、协议扩展并向协议添加关联类型,并在每个 class 中添加相应的类型限制。
protocol Shelter {
var animalType: Any.Type { get }
func usefulMethod() -> String
}
extension Shelter {
func usefulMethod() -> String {
return "useful"
}
}
class AnimalShelter: Shelter {
let animalType: Any.Type = Animal.self
func take(x: animalType) {}
}
class DogShelter: Shelter {
let animalType: Any.Type = Dog.self
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: animalType) {}
}
您可以使用具有关联类型的协议:
protocol Shelter {
associatedtype AnimalType
func take(x: AnimalType)
}
extension Shelter {
func usefulMethod(...)
}
class AnimalShelter : Shelter {
typealias AnimalType = Animal
func take(x: Animal) { ... }
}
class DogShelter : Shelter {
typealias AnimalType = Dog
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: Dog) {}
}
我需要创建一个新的 class。它的一些功能已经在另一个 class 中,从领域的角度来看,继承它是有意义的。问题是有一个方法必须在它的参数类型上有更多的限制,因为 LSP (Liskov substitution principle) 你不能覆盖它。
到目前为止有代码,我可能会更改,
为了更好的解释让我举一个简单的例子:
我有 AnimalShelter
,我需要实施 DogShelter
。
class AnimalShelter {
func usefulMethod(...) {}
func take(x: Animal) {}
}
class DogShelter {
var dogMedianCuteness: String = "normal (= very cute)"
func usefulMethod(...) {}
func take(x: Dog) {}
}
解决方案 1:子class
如果DogShelter
是AnimalShelter
的子class那么它将免费得到usefulMethod(...)
,但是继承的方法take(x: Animal)
不能覆盖并污染了 DogShelter 的 API,应该什么都不做或抛出错误。
class AnimalShelter {
func usefulMethod(...) {}
func take(x: Animal) {}
}
class DogShelter: AnimalShelter {
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: Dog) {}
}
方案二:协议+协议扩展
如果AnimalShelter
和DogShelter
实现一个协议,从域的角度来看并不精确,但是共享代码usefulMethod(...)
可以在协议中实现 extension
.
protocol UsefulThing {
func usefulMethod(...)
}
extension UsefulThing {
func usefulMethod(...) { ... }
}
class AnimalShelter: UsefulThing {
func take(x: Animal) {}
}
class DogShelter: UsefulThing {
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: Dog) {}
}
解决方案 3:泛化,创建另一个超级class
问题出在 take(x: T)
方法上,它在 DogShelter
中更为专业。将它从 AnimalShelter 中移除将允许继承而不会出现问题,但是到目前为止使用 AnimalShelter 的所有内容都必须由一个新的 subclass AnyAnimalShelter: AnimalShelter
替换,它有问题 take(x: Animal) {}
class AnimalShelter {
usefulMethod(...) {}
}
class AnyAnimalShelter: AnimalShelter {
take(x: Animal) {}
}
class DogShelter: AnimalShelter {
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: Dog) {}
}
解决方案 4:组合
从域的角度来看继承确实有意义,因此老板认为最好保留。
所以我从 AnimalShelter
获得了代码并允许更改它,尽管它会引起人们的注意,为什么我多年来会完美地更改 运行 的代码。我需要一个关于 take(x: Animal)
方法在 AnimalShelter
中有缺陷的抽象原因。不仅要有充分的理由反对它,而且还要在将来避免它 classes.
如果我无法更改 使用 AnimalShelter
或 AnimalShelter
本身的代码,那将是一个真正的问题。
问题
someone/I 应该如何建模?
Sweeper 建议的解决方案。
class Animal {}
class Dog: Animal {}
当没有任何存储属性时,使用泛型 + class 带有类型约束(where 子句)的扩展。
class AnimalShelter<T: Animal> {
func usefulMethod(...) {}
func take(x: T) {}
}
否则使用解决方案 2:协议、协议扩展并向协议添加关联类型,并在每个 class 中添加相应的类型限制。
protocol Shelter {
var animalType: Any.Type { get }
func usefulMethod() -> String
}
extension Shelter {
func usefulMethod() -> String {
return "useful"
}
}
class AnimalShelter: Shelter {
let animalType: Any.Type = Animal.self
func take(x: animalType) {}
}
class DogShelter: Shelter {
let animalType: Any.Type = Dog.self
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: animalType) {}
}
您可以使用具有关联类型的协议:
protocol Shelter {
associatedtype AnimalType
func take(x: AnimalType)
}
extension Shelter {
func usefulMethod(...)
}
class AnimalShelter : Shelter {
typealias AnimalType = Animal
func take(x: Animal) { ... }
}
class DogShelter : Shelter {
typealias AnimalType = Dog
var dogMedianCuteness: String = "normal (= very cute)"
func take(x: Dog) {}
}