【问题标题】:Template Method and Remove If Statements模板方法和删除 If 语句
【发布时间】:2018-12-06 01:31:21
【问题描述】:

所以,我学习设计模式,学习模板方法。

根据我的理解,它是一组方法(骨架)包装在抽象类(如果通过遗产完成)上的方法(操作)中,不同的具体子类在其中编写自己的这些方法的实现(不是全部)。

但是我有一个疑问,如果某些具体的骨骼没有使用骨骼的一些,也许是 2 种方法会发生什么?,这里我有一个我做的例子,它完全违反了 SRP:

using System;

namespace TemplatePattern
{
    public abstract class Coffee
    {

        public void MakeCoffee()
        {
            HeatWater();

            PutCoffee();

            if (HaveMilk())
            {
                PutMilk();
            }

            if (HaveGratedChocolate())
            {
                PutGratedChocolate();
            }

            PutSweetener();

            ServeCoffee();
        }

        internal void HeatWater()
        {
            Console.WriteLine($"I heated the water");
        }

        internal void ServeCoffee()
        {
            Console.WriteLine($"Coffee Served");
        }

        internal void PutCoffee()
        {
            Console.WriteLine("I put 2 spoons of Coffee");
        }

        internal virtual void PutMilk() { }

        internal virtual void PutGratedChocolate() { }

        internal abstract void PutSweetener();

        public virtual bool HaveMilk()
        {
            return false;
        }

        public virtual bool HaveGratedChocolate()
        {
            return false;
        }
    }
}

具体类 SimpleCoffeeWithMilk:

using System;
namespace TemplatePattern
{
    public class SimpleCoffeWithMilk : Coffee
    {

        public override bool HaveMilk()
        {
            return true;
        }

        internal override void PutSweetener()
        {
            Console.WriteLine($"I put 1 spoon of Sugar");
        }

        internal override void PutMilk()
        {
            Console.WriteLine($"I put 100Cc of Milk");
        }
    }
}

另一个具体类:

using System;

namespace TemplatePattern
{
    public class CoffeeWithChocolate : Coffee
    {
        public override bool HaveGratedChocolate()
        {
            return true;
        }

        internal override void PutGratedChocolate()
        {
            Console.WriteLine("Put Chocolate");
        }

        internal override void PutSweetener()
        {
            Console.WriteLine($"Put Sugar");
        }

    }
}

主要入口点:

using System;

namespace TemplatePattern
{
    class Program
    {
        static void Main(string[] args)
        {
            SimpleCoffeWithMilk coffeWithMilk = new SimpleCoffeWithMilk();
            CoffeeWithChocolate coffeeWithChocolate = new CoffeeWithChocolate();

            coffeWithMilk.MakeCoffee();

            Console.WriteLine("\n\n");

            coffeeWithChocolate.MakeCoffee();

            Console.ReadLine();

        }
    }
}

想法是摆脱那些If语句,有没有办法用模板方法做到这一点,其中一些方法根据具体类执行?

我正在考虑创建像ICoffeeWithMilk 这样的接口,上面带有PutMilk() 方法,并在我的SimpleCoffeeWithMilk 具体类上实现该接口,但是看着UML,我看到的模板方法不依赖于接口。

编辑:现在想来,不能用接口,因为模板方法在操作中涉及到一组有序的方法,所以这些方法在操作之外。

编辑 2: 好的,我在想 PutMilk()PutGratedChocolate() 是 Hook 方法,也许我可以将它们设为抽象方法,并且在具体类中不要放置任何实现,甚至没有未实现异常类。有了这个,它们可以在我的模板方法中没有任何 if 语句的情况下存在。但我认为,我不确定这是否违反了 Liskov 替换原则。

编辑 3: 嗯……我又在想,得出的结论是,如果这些方法是虚拟的并且在抽象类上没有实现,我不应该担心问,如果具体类使用那个方法,那么你写算法,如果你不使用,那就不要,它什么也不做,它会进入下一步。

【问题讨论】:

    标签: c# design-patterns template-method-pattern


    【解决方案1】:

    只需删除 if 语句并让具体通过执行它们来做出决定,如下所示:

    public abstract class Coffee
    {
        public void MakeCoffee()
        {
            PutMilk();
        }
    
        protected abstract void PutMilk();
    }
    
    public class SimpleCoffeWithMilk : Coffee
    {    
        public override void PutMilk()
        {
            Console.WriteLine($"I put 100Cc of Milk");
        }
    }
    
    public class SimpleCoffeNoMilk : Coffee
    {    
        public override void PutMilk()
        {
            //no op
        }
    }
    

    关于您对这个解决方案的担忧,我认为它不违反 Liskov。 LSP 声明子类型必须可以替代它的基类的任何其他继承者,这些情况就是这样。你可以打电话给PutMilk,他们会把所有适合他们的专业化接口的牛奶放进去。这些变化都不会影响调用方法。需要说明的是,您可以设计一个可以做到这一点的示例,但在这种情况下,您不会在那里遇到问题。

    【讨论】:

    • 嘿@tallseth 感谢您的回答!我们的步骤(MakeCoffee 中的方法)是否比抽象的虚拟更好?因为如果我们的 MakeCoffee 操作需要所有这些方法并且按照特定的顺序,当我们接触到那些抽象且未实现的...我的意思是,我们可以将不需要的方法的主体留为空白,这就是我认为的原因违反了 Liskov (就像鸭子的励志图片,如果你的鸭子不需要电池,那么你的抽象是不正确的)
    • 在这种情况下肯定存在抽象错误的风险。在这个例子中,我不认为你会这样做:制作咖啡的本质是有一个添加牛奶的步骤,有些人通过说“不,谢谢”来执行该步骤。没什么大不了。至于使方法abstract vs virtual 和空,都可以。为您的案件的具体情况做些让生活变得轻松的事情。 (抱歉回复晚了,今天才看到这个!)
    【解决方案2】:

    您可以将选择语句(在这种情况下为 if 语句)转移到您的模板中,但如果您需要检查某些条件,则无法删除它们。天气你这样做

    public abstract class Coffee
    {
        public abstract bool HaveMilk();
    
        public void MakeCoffee()
        {
            if (HaveMilk())
            {
                PutMilk();
            }
        }
    }
    
    public class SimpleCoffeWithMilk : Coffee
    {
        public bool HaveMilk()
        {
            return true;
        }
    
        public override void PutMilk()
        {
            Console.WriteLine($"I put 100Cc of Milk");
        }
    }
    

    或者像这样

    public abstract class Coffee
    {
        public void MakeCoffee()
        {
            PutMilk();
        }
    }
    
    public class SimpleCoffeWithMilk : Coffee
    {
        public bool HaveMilk()
        {
            return true;
        }
    
        public override void PutMilk()
        {
            if (HaveMilk())
            {
                Console.WriteLine($"I put 100Cc of Milk");
            }
        }
    }
    

    我坚信后者是您正在寻找的。​​p>

    【讨论】:

    • 拥有一个名为 SimpleCoffeWithMilk 的类和一个返回 true 的方法 HaveMilk 只是多余的。
    猜你喜欢
    • 2020-03-18
    • 1970-01-01
    • 2016-10-25
    • 1970-01-01
    • 2019-02-20
    • 2014-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多