如何将某些行为限制为特定类的实例子集

本文关键字:子集 实例 | 更新日期: 2023-09-27 18:32:11

我正在研究配方应用程序的域模型并遇到了问题。

该应用程序具有多个能够充当成分的实体,其中两个是:ProductRecipe(配方可以是其他配方中的成分)。通常,我会将与成分相关的功能封装到每个实体都可以实现的接口中。问题在于,虽然所有产品实例都可以是成分,但只有一部分配方实例可以是成分

interface IIngredient
{
    void DoIngredientStuff();
}
class Product : IIngredient
{
    void DoIngredientStuff()
    {
        // all Products are ingredients - do ingredient stuff at will
    }
}
class Recipe : IIngredient
{
    public IEnumerable<IIngredient> Ingredients { get; set; }
    void DoIngredientStuff()
    {
        // not all Recipes are ingredients - this might be an illegal call
    }
}

如何重构此模型以支持只有某些配方实例应能够充当成分的要求?

如何将某些行为限制为特定类的实例子集

另一种选择,如果它在您的类树中工作,可以为这两种类型的食谱提供单独的类。请注意,如果要通过更多属性来区分对象,则此方法效果不佳。

class Recipe {
    public IEnumerable<IIngredient> Ingredients { get; set; }
}
class UsefulRecipe : Recipe, IIngredient
{
    void DoIngredientStuff()
    {
        // not all Recipes are ingredients - this might be an illegal call
    }
}
对我来说听起来

像是一个设计问题。如果你必须开始对IsIngredient进行测试,我认为你的设计出了问题。当您遇到其他特殊情况时会发生什么?然后另一个?你会继续添加特殊的 If 测试还是一个大的开关语句?这打破了开-闭原则。

如何偏爱组合而不是继承?您可能还想查看策略模式...

这里的核心问题是配方不应该实施 IIngredient......因为并非所有食谱都实现了 IIngient 行为......

interface IIngredient 
{ 
    bool IsIngredient { get; }
    void DoIngredientStuff(); 
} 
class Recipe : IIngredient 
{ 
    public IEnumerable<IIngredient> Ingredients { get; set; } 
    bool IsIngredient {
       get { return true; // true if it is, false if it isn't }
    }   
    void DoIngredientStuff() 
    { 
      if (IsIngredient) {
        // do whatever
      }
    } 
} 

我可能会使用组合来创建一个可以组合 2 个(也许更多)食谱的CompositeRecipe。 没有将所有食谱都用作新食谱的基础的原因是什么? 您始终可以添加一个布尔属性来建议配方是一个完整的配方(IsCompleteRecipe),然后由您如何构建应用程序和应用程序逻辑来确定配方是否应与其他配方组合。

如果只有一些配方是成分,那么这似乎是继承的经典案例,IIngredient是在子类上实现的:

class Product : IIngredient
{
    void DoIngredientStuff();
}
class Recipe
{
    public IEnumerable<IIngredient> Ingredients { get; set; }
}
class IngredientRecipe : Recipe, IIngredient
{
    void DoIngredientStuff();
}

您可以使用适配器使食谱表现为成分:

class RecipeIngredient : IIngredient
{
    private readonly Recipe _recipe;
    public RecipeIngredient(Recipe recipe) {
        _recipe = recipe;
    }
    public void DoIngredientStuff() {
        // Do stuff with the recipe.
    }
}