【问题标题】:Subtypes of an interface only compatible with a subtype of another interface一个接口的子类型只兼容另一个接口的子类型
【发布时间】:2015-08-18 01:48:05
【问题描述】:

我一直在为一个问题摸不着头脑,但我仍然不知道什么是最好的解决方案。由于应用程序领域非常技术性,我将通过一个简单的示例来说明我的问题。

假设我有以下接口:

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
    }
}

这意味着每次我提供 DogCat 实例时,我都必须验证接收到的 DogFoodCatFood 实例,如果这不是正确的类型,则抛出异常食物。

这对我来说很难闻,而且我很肯定违反了 Liskov 替换原则!

是否有设计模式,或者有什么优雅的方式来管理这种情况?

【问题讨论】:

标签: oop design-patterns architecture solid-principles


【解决方案1】:

看来您正在使用 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
    }
}

【讨论】:

    【解决方案2】:

    由于这个问题似乎与语言无关,我将在 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!");
        }
    }
    

    结果:

    例外

    你一定是在开玩笑吧朋友!我不吃那个!

    或者,让AnimalFoods 实现一个标志枚举(位域),它指定可以吃它们的动物的类型。

    无论哪种方式(一种食物属性或可以吃我属性的动物):

    • 您不必进行类型检查

    • 没有编译时限制

    • 食物的类型或可以吃的动物很好 封装到 AnimalFood 类中

    这也可以与其他地方建议的工厂理念相结合。

    【讨论】:

      【解决方案3】:

      为了用泛型来补充公认的答案,让所有 Animal 实例能够通过工厂方法设计模式创建自己的食物类型可能会很有用。

      这是 GoF 模式:

      以下是如何将其应用于您的案例:

      【讨论】:

        猜你喜欢
        • 2017-05-29
        • 2018-07-23
        • 2021-07-22
        • 2022-11-28
        • 1970-01-01
        • 1970-01-01
        • 2023-03-11
        • 2017-05-28
        • 1970-01-01
        相关资源
        最近更新 更多