接口的子类型仅与另一个接口的子类型兼容
Subtypes of an interface only compatible with a subtype of another interface
我一直在为一个问题绞尽脑汁,但我仍然不知道什么是最好的解决方案。由于应用领域是非常技术性的,我将用一个简单的例子来说明我的问题。
假设我有以下接口:
public interface Animal {
public void feed(AnimalFood food);
}
public interface AnimalFood {
// some methods
}
以及以下两个 类 实现接口:
public class DogFood implements AnimalFood {
// some methods
}
public class CatFood implements AnimalFood {
// some methods
}
public class Dog implements Animal {
public void feed(AnimalFood food){
// can only eat dog food
}
}
public class Cat implements Animal {
public void feed(AnimalFood food){
// can only eat cat food
}
}
这意味着每次我提供 Dog
或 Cat
实例时,我都必须验证接收到的 DogFood
或 CatFood
实例并抛出如果这不是正确的食物,则例外。
这对我来说很难闻,我很确定违反了 Liskov 替换原则!
是否有设计模式,或者什么是管理这种情况的优雅方式?
您似乎正在使用 Java。如果是这样,您可以使用泛型来限制 feed
中的参数类型
public interface Animal<T extends AnimalFood> {
void feed(T food);
}
public class Dog implements Animal<DogFood> {
public void feed(DogFood food){
// can only eat dog food
}
}
public class Cat implements Animal<CatFood> {
public void feed(CatFood food){
// can only eat cat food
}
}
由于这个问题似乎与语言无关,我将用 C# 回答,但这些想法将适用于 Java 和类似的问题。
我们可以将IAnimalFood
声明为通用接口,但这会产生编译时间错误,而不是运行您要求的时间例外,如果您尝试将猫食喂给狗:
void Main()
{
new Cat().Feed(new UserQuery.CatFood());
new Dog().Feed(new UserQuery.CatFood()); // won't compile
}
public interface IAnimalFood { }
public class DogFood : IAnimalFood { }
public class CatFood : IAnimalFood { }
public interface IAnimal<TFood> where TFood : IAnimalFood
{
void Feed(TFood food);
}
public class Dog : IAnimal<DogFood> {
public void Feed(DogFood food){
// can only eat dog food
}
}
public class Cat : IAnimal<CatFood> {
public void Feed(CatFood food){
// can only eat cat food
}
}
似乎没有任何合理的 运行 时间替代方法来检查所提供食物的 类型 除了 声明模式 我以前成功使用过:添加到食物类型 属性 指定它是什么食物:
void Main()
{
new Dog().Feed(new UserQuery.CatFood());
}
public enum FoodType
{
CatFood,
DogFood,
BirdFeed
}
public interface IAnimalFood {
FoodType TypeOfFood { get; }
}
public class DogFood : IAnimalFood {
public FoodType TypeOfFood { get { return FoodType.DogFood; } }
}
public class CatFood : IAnimalFood
{
public FoodType TypeOfFood { get { return FoodType.CatFood; } }
}
public interface IAnimal
{
void Feed(IAnimalFood food);
}
public class Dog : IAnimal {
public void Feed(IAnimalFood food){
// can only eat dog food
if (food.TypeOfFood != FoodType.DogFood)
throw new Exception("You must be joking pal! I don't eat that!");
}
}
结果:
Exception
You must be joking pal! I don't eat that!
或者,让 AnimalFood
s 实现一个标志枚举(位域),它指定可以吃它们的动物类型。
无论哪种方式(一种食物 属性 或一种可以吃掉我的动物 属性):
您不必进行类型检查
没有编译时约束
能吃的食物种类或动物很好
封装到 AnimalFood class
这也可以与其他地方建议的工厂想法相结合。
为了用泛型补充已接受的答案,让所有 Animal 实例都能够通过工厂方法设计模式创建自己的食物类型可能很有用。
这是 GoF 模式:
以下是将其应用于您的案例的方法:
我一直在为一个问题绞尽脑汁,但我仍然不知道什么是最好的解决方案。由于应用领域是非常技术性的,我将用一个简单的例子来说明我的问题。
假设我有以下接口:
public interface Animal {
public void feed(AnimalFood food);
}
public interface AnimalFood {
// some methods
}
以及以下两个 类 实现接口:
public class DogFood implements AnimalFood {
// some methods
}
public class CatFood implements AnimalFood {
// some methods
}
public class Dog implements Animal {
public void feed(AnimalFood food){
// can only eat dog food
}
}
public class Cat implements Animal {
public void feed(AnimalFood food){
// can only eat cat food
}
}
这意味着每次我提供 Dog
或 Cat
实例时,我都必须验证接收到的 DogFood
或 CatFood
实例并抛出如果这不是正确的食物,则例外。
这对我来说很难闻,我很确定违反了 Liskov 替换原则!
是否有设计模式,或者什么是管理这种情况的优雅方式?
您似乎正在使用 Java。如果是这样,您可以使用泛型来限制 feed
public interface Animal<T extends AnimalFood> {
void feed(T food);
}
public class Dog implements Animal<DogFood> {
public void feed(DogFood food){
// can only eat dog food
}
}
public class Cat implements Animal<CatFood> {
public void feed(CatFood food){
// can only eat cat food
}
}
由于这个问题似乎与语言无关,我将用 C# 回答,但这些想法将适用于 Java 和类似的问题。
我们可以将IAnimalFood
声明为通用接口,但这会产生编译时间错误,而不是运行您要求的时间例外,如果您尝试将猫食喂给狗:
void Main()
{
new Cat().Feed(new UserQuery.CatFood());
new Dog().Feed(new UserQuery.CatFood()); // won't compile
}
public interface IAnimalFood { }
public class DogFood : IAnimalFood { }
public class CatFood : IAnimalFood { }
public interface IAnimal<TFood> where TFood : IAnimalFood
{
void Feed(TFood food);
}
public class Dog : IAnimal<DogFood> {
public void Feed(DogFood food){
// can only eat dog food
}
}
public class Cat : IAnimal<CatFood> {
public void Feed(CatFood food){
// can only eat cat food
}
}
似乎没有任何合理的 运行 时间替代方法来检查所提供食物的 类型 除了 声明模式 我以前成功使用过:添加到食物类型 属性 指定它是什么食物:
void Main()
{
new Dog().Feed(new UserQuery.CatFood());
}
public enum FoodType
{
CatFood,
DogFood,
BirdFeed
}
public interface IAnimalFood {
FoodType TypeOfFood { get; }
}
public class DogFood : IAnimalFood {
public FoodType TypeOfFood { get { return FoodType.DogFood; } }
}
public class CatFood : IAnimalFood
{
public FoodType TypeOfFood { get { return FoodType.CatFood; } }
}
public interface IAnimal
{
void Feed(IAnimalFood food);
}
public class Dog : IAnimal {
public void Feed(IAnimalFood food){
// can only eat dog food
if (food.TypeOfFood != FoodType.DogFood)
throw new Exception("You must be joking pal! I don't eat that!");
}
}
结果:
Exception
You must be joking pal! I don't eat that!
或者,让 AnimalFood
s 实现一个标志枚举(位域),它指定可以吃它们的动物类型。
无论哪种方式(一种食物 属性 或一种可以吃掉我的动物 属性):
您不必进行类型检查
没有编译时约束
能吃的食物种类或动物很好 封装到 AnimalFood class
这也可以与其他地方建议的工厂想法相结合。
为了用泛型补充已接受的答案,让所有 Animal 实例都能够通过工厂方法设计模式创建自己的食物类型可能很有用。
这是 GoF 模式:
以下是将其应用于您的案例的方法: