Шаблонный метод и операторы If If - PullRequest
0 голосов
/ 27 июня 2018

Итак, я изучаю шаблоны проектирования, и я изучаю метод шаблонов.

Из того, как я это понял, это набор методов (скелет), обернутый в метод (операцию) над абстрактным классом (если выполняется через наследие), где разные конкретные подклассы пишут свою собственную реализацию этих методов (не все они).

Но я сомневаюсь, что произойдет, если некоторые, может быть, 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, метод Template для того, что я видел, не полагается на интерфейсы.

Редактировать: Теперь, когда я об этом думаю, я не могу использовать интерфейс, так как метод шаблона относится к набору упорядоченных методов в операции, поэтому эти методы не работают.

Edit 2: Хорошо, я думал, что PutMilk() и PutGratedChocolate() являются методами Hook, возможно, я могу сделать их абстрактными методами и в конкретные классы не помещать какую-либо реализацию, даже не реализован класс исключений. При этом они могут существовать без каких-либо утверждений в моем методе шаблона. Но я думаю, я не уверен, что это нарушает принцип замещения Лискова.

Редактировать 3: Хорошо ... Я снова подумал и пришел к выводу, что, если эти методы являются виртуальными и не имеют реализации в абстрактном классе, мне не следует беспокоиться о том, чтобы спросить Если конкретный класс использует этот метод, то вы пишете алгоритм, если вы его не используете, то не выполняете, и он ничего не будет делать, он перейдет к следующему шагу.

Ответы [ 2 ]

0 голосов
/ 10 июля 2018

Просто удалите операторы 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
    }
}

Что касается вашей озабоченности этим решением, я не думаю, что оно нарушает Лискова. LSP заявляет, что подтип должен быть заменяем для любых других наследников своего базового класса, каковыми являются эти случаи. Вы можете позвонить PutMilk для любого из них, и они положат все молоко, соответствующее их специализации интерфейса. Ни один из этих вариантов не влияет на вызывающий метод. Чтобы было ясно, вы можете придумать пример, который бы это сделал, но в этом случае вы не столкнетесь с проблемой там.

0 голосов
/ 27 июня 2018

Вы можете перенести оператор выбора (если в этом случае оператор) в ваши шаблоны, но не можете избавиться от них, если вам нужно проверить какое-либо условие. Погода вы делаете это так

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");
        }
    }
}

Я твердо верю, что последнее - это то, что вы ищете.

...