Дизайн декоратора и фабричный дизайн - PullRequest
1 голос
/ 28 марта 2019

Я пытаюсь выяснить, как использовать пользовательский ввод и вывод определенной информации в зависимости от пользовательского ввода. Может ли кто-нибудь привести простой пример использования шаблона декоратора для простой пиццерии, например?

Так что я знаю, что делать с шаблоном декоратора, это просто часть пользовательского ввода, которая застряла у меня. Итак, предположим, что пользователь хочет сделать пиццу, он сначала выберет размер пиццы, а затем добавит ASB много начинки по своему усмотрению. Затем, когда они закончат, они увидят их общую цену за то, что они добавили, а также то, что они добавили (например, получение). Это на Яве.

Ответы [ 5 ]

1 голос
/ 29 марта 2019

Декоратор - это класс, который расширяет функциональность другого класса. Декоратор обычно реализует тот же интерфейс, так что декорированный объект можно использовать вместо базового. Хорошим примером является компрессор и / или шифратор, применяемый к файлу или, в более общем случае, к реализации потока данных, как показано в ответ by @ nits.kk .

В случае пиццы мы должны определить, какое поведение нам нужно:

public interface Pizza {
    public String getIngredients();  // comma separated
    public double getTotalPrice();
}

Пицца состоит из двух основных типов ингредиентов: обязательная единая основа и необязательные множественные начинки. Каждый ингредиент имеет свою цену.

public class PizzaIngredient {
    private double getPrice() {
        return 0.0;
    }
}

База для пиццы сама по себе является самой простой пиццей, поэтому она должна реализовывать интерфейс Pizza. У него есть размер как его атрибут (и цена, конечно). Мы могли бы реализовать размер как отдельный класс, но я не считаю его разумным - он недостаточно универсален, чтобы быть полезным вне вселенной пиццы, и недостаточно сложен, чтобы заслужить свой собственный интерфейс.

public class PizzaBase extends PizzaIngredient implements Pizza {
    public PizzaBase(String size) {
        this.size = size;
    }

    public String getIngredients() {
        return size + " base";  // the only ingredient is this base
    }
    public double getTotalPrice() {
        return getPrice();      // the base-only pizza costs the base cost
    }
    private double getPrice() {
        if(size == "small")
            return 2.0;
        if(size == "medium")
            return 2.5;

        return 3.0;            // large and undefined
    }

    private final String size;
}

Теперь нам нужны начинки. Они будут добавлены поверх пиццы в качестве декораторов: пицца плюс топпинг - это тоже пицца, поэтому самый верхний топинг будет представлять всю композицию. Список ингредиентов такой пиццы - это список ингредиентов основной пиццы, а также наименование ее самой верхней части. Аналогично общая цена.

public class PizzaTopping extends PizzaIngredient implements Pizza {
    public PizzaTopping(String name, Pizza pizza) {
        this.name = name;
        this.pizza = pizza;
    }

    public String getIngredients() {
        return pizza.getIngredients() + ", " + getName();
    }
    public double getTotalPrice() {
        return pizza.getTotalPrice() + getPrice();
    }
    public String getName() {
        return name;
    }

    private final String name;
    private final Pizza pizza;
}

Давайте определим некоторые конкретные начинки:

public class MozzarellaTopping extends PizzaTopping {
    public MozzarellaTopping(Pizza pizza) {
        super("mozzarella", pizza);
    }

    private double getPrice() {
        return 0.5;
    }
}

public class MushroomTopping extends PizzaTopping {
    public MushroomTopping(Pizza pizza) {
        super("mushroom", pizza);
    }

    private double getPrice() {
        return 2.0;
    }
}

public class PepperoniTopping extends PizzaTopping {
    public PepperoniTopping(Pizza pizza) {
        super("pepperoni", pizza);
    }

    private double getPrice() {
        return 1.5;
    }
}

public class GreenOliveTopping extends PizzaTopping {
    public GreenOliveTopping(Pizza pizza) {
        super("green olive", pizza);
    }

    private double getPrice() {
        return 1.2;
    }
}

Хорошо, это много классов; но какой из них и когда нам понадобится?

Здесь фабрика присоединяется к команде. Фабрика - это класс для создания объектов некоторых классов. Он используется, чтобы скрыть детали создания за кулисами, особенно когда созданные объекты являются сложными или имеют разные конкретные классы. Когда результирующие объекты создаются как автономные сущности, фабричный класс может быть просто пространством имен со статическим методом в нем. OTOH, если объекты создаются в некотором контексте, фабрика может быть объектом, связанным с контекстом (например, параметризованным) и использующим этот контекст в процессе создания.

Мы можем использовать фабрику для создания ингредиентов для пиццы по желанию, в соответствии с пользовательской информацией. Большинство ингредиентов - это начинки, которые нужно наносить поверх уже существующей пиццы, поэтому давайте передадим пиццу на фабрику, чтобы в результате получить украшенную пиццу. Особый случай - создание базы для пиццы, которая не наносится на другую пиццу; в этом случае параметр pizza игнорируется, поэтому мы можем передать null.

public class PizzaFactory {
    public static Pizza getPizza(Pizza pizza, String name)
    {
        if ( name.equals("small") || name.equals("medium") || name.equals("large") )
            return new PizzaBase(name);
        else if ( name.equals("mozzarella") )
            return new MozzarellaTopping(pizza);   // add topping to the pizza
        else if ( name.equals("mushroom") )
            return new MushroomTopping(pizza);
        else if ( name.equals("pepperoni") )
            return new PepperoniTopping(pizza);
        else if ( name.equals("green olive") )
            return new GreenOliveTopping(pizza);

        return null;
    }
}

Теперь мы готовы построить нашу пиццу.

class PizzaTest {
    public static void main(String[] args) {
        DecimalFormat priceFormat = new DecimalFormat("#.##");

        Pizza pizza;

        pizza = PizzaFactory.getPizza(null, "small");
        System.out.println("The small pizza is: " + pizza.getIngredients());
        System.out.println("It costs " + priceFormat.format(pizza.getTotalCost()));

        pizza = PizzaFactory.getPizza(null, "medium");
        pizza = PizzaFactory.getPizza(pizza, "mozzarella");
        pizza = PizzaFactory.getPizza(pizza, "green olive");

        System.out.println("The medium pizza is: " + pizza.getIngredients());
        System.out.println("It costs " + priceFormat.format(pizza.getTotalCost()));

        String largePizzaOrder[] = { "large", "mozzarella", "pepperoni",
                                     "mushroom", "mozzarella", "green olive" };

        pizza = null;
        for (String cmd : largePizzaOrder)
            pizza = PizzaFactory.getPizza(pizza, cmd);

        System.out.println("The large pizza is: " + pizza.getIngredients());
        System.out.println("It costs " + priceFormat.format(pizza.getTotalCost()));
    }
}

Предупреждение: в приведенном выше коде есть некоторые подводные камни и ярлыки.

Наиболее важным является отсутствие проверки ввода: при получении неожиданной команды фабрика вернет null, что вызовет сбой при будущем использовании getIngredients() или getTotalCost().

Еще одно - жесткое кодирование цен в конкретные классы. Фактическое решение должно было бы использовать некоторый прайс-лист и получать цены либо при создании ингредиента (и хранить выбранные цены в объектах ингредиента), либо при использовании, т. Е. В методе getCost() (который потребовал бы некоторого доступа к прайс-листу от ингредиенты пиццы).

1 голос
/ 28 марта 2019

Давайте посмотрим на пункты ниже, чтобы начать с

  1. Образец декоратора в чистом виде намеревается enhance existing behavior of an object at run time without destroying the existing interface of the object.
  2. Декорирование подразумевает улучшение существующего поведения объекта.
  3. Декорированный объект имеет тот же (базовый) интерфейс, что и декорируемый базовый объект.

Вопрос: Объект извлекается из его класса, который является временем компиляции. Теперь, как бы вы продолжали улучшать поведение?

Ответ: Используя шаблон Decorator, также известный как обертка.

Пример: у вас есть файл, который может быть зашифрован, допустим, методов шифрования в настоящее время 5, результатом будет зашифрованный файл. Зашифрованный файл может быть снова зашифрован. Кроме того, давайте предположим, что существует 5 способов заархивировать файл, который впоследствии может также увеличиться. Файл может быть зашифрован с помощью methodEA, затем может быть заархивирован с помощью MethodZA, затем снова может быть зашифрован с помощью methodEB, аналогичные последовательности могут создавать разные файлы результатов.

Один из хороших способов - как показано ниже.

public class TextFile{
       public void create(){/*somecode*/};
       public void format(){//code for default plain text};
}

public class AEncryptedFile extends TextFile{
        private TextFile wrapped;
        public AEncryptedFile(TextFile file){
              this.wrapped = file;
        }
        public void format(){
               super.format();
               //add enhacements for encryption type A
        }
}

public class BEncryptedFile extends TextFile{
        private TextFile wrapped;
        public BEncryptedFile(TextFile file){
              this.wrapped = file;
        }
        public void format(){
               super.format();
               //add enhacements for encryption type B
        }
}

public class AZippedFile extends TextFile{
        private TextFile wrapped;
        public BEncryptedFile(TextFile file){
              this.wrapped = file;
        }
        public void format(){
               super.format();
               //add enhacements for zip type A
        }
}

public class BZippedFile extends TextFile{
        private TextFile wrapped;
        public BEncryptedFile(TextFile file){
              this.wrapped = file;
        }
        public void format(){
               super.format();
               //add enhacements for zip type B
        }
}

public void UserClass{
    public static void main(String[] args){
          TextFile file = new BZippedFile(new AEncryptedFile(new TextFile()));
          file.format();
}

В приведенном выше примере кода можно сказать

Объект TextFile был декорирован (путем переноса) объектом AEncryptedFile, который дополнительно декорируется BZippedFile, в каждом из этих украшений было сделано дополнительное усовершенствование существующего объекта

Таким образом, существующий объект TextFile может быть передан различным методам во время выполнения, а объект может быть оформлен путем обертывания его в другой объект подтипа TextFile.

Примечание : реализация шаблона Decorator имеет структуру LinkedList.

0 голосов
/ 29 марта 2019
0 голосов
/ 28 марта 2019

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

Лучшим примером пиццы было бы взятькласс пиццы, который может делать следующие вещи:

  • подают пиццу
  • подают безалкогольные напитки

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

public class Pizzeria {
    public String orderPizza() {
        System.out.println("you ordered a pizza");
    }

    public String orderSoftDrink() {
        System.out.println("you ordered a soft drink");
    }
}

Чтобы реализовать здесь шаблон декоратора, мы обертываем существующий класс Pizzeria, а затем добавляем новую функцию public String orderPizza () {System.out.println («Вы заказали пиццу»);} public String orderSoftDrink () {System.out.println («Вы заказали безалкогольный напиток»);} ality:

public class NewPizzeria {
    private Pizzeria pizzeria;

    public NewPizzeria() {
        pizzeria = new Pizzeria();
    }

    public String orderPizza() {
        pizzeria.orderPizza();
    }

    public String orderSoftDrink() {
        pizzeria.orderSoftDrink();
    }

    public String orderSalad() {
        System.out.println("you ordered a salad");
    }
}

Ключевым моментом здесь является то, что класс NewPizzeria "владеет" своим собственным Pizzeria объектом.По большей части, он просто отражает ту же функциональность, которую уже имеет Pizzeria.Но он также добавляет некоторые новые собственные функциональные возможности.

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

0 голосов
/ 28 марта 2019

Концептуально, в шаблоне декоратора выходные данные одной обработки идут как входные данные для другой обработки.

Итак, в вашем случае это должно быть так:

getToppingFoo(getToppingBar(...(getXBaseSizePizzaCost())

, который разрешается в:

FooToppingCost + (BarToppingCost + ... ( Cost of pizza with base of X size )

Далее, вы можете определить фабричный класс для получения Pizzaобъекта различных размеров, скажем, Standard, Medium, Large.Логика одинакова, независимо от того, какой язык вы выбираете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...