Шаблоны проектирования: простая модель утки (начало работы)

Шаблоны проектирования

Я впервые пишу статью, пожалуйста, поправьте меня, если что-то не так с написанием.

Возможно, это будет продолжительная серия шаблонов проектирования, хахаха.

Эта книга, перенесенная в начало шаблона проектирования, действительно интересна и проста для понимания.

Прежде всего, чтобы начать работу с шаблонами проектирования, вам нужно посмотреть на простое приложение с имитацией утки, чтобы начать

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


Все утки квакают и плавают, поэтому пусть суперкласс обрабатывает эту часть кода реализации.

Каждый подтип утки отвечает за реализацию собственного поведения display() для отображения своего вида на экране.


Теперь, когда супервайзеры решили, что программе моделирования нужны летающие утки, Джо сказал им, что может сделать это за неделю, в чем сложность? У Джо должен быть метод fly() в классе уток, тогда все утки будут летать, и он с радостью пересечет

Но случилось ужасное, начальник заметил много резиновых уточек, летающих по экрану во время просмотра, и сказал ему искать другую работу.

Оказалось, что Джо упустил из виду одну вещь, не все подклассы Duck могут летать, Джо добавляет новое поведение в класс Duck, чтобы некоторые подклассы, которые не подходят для этого поведения, также имели это поведение. Теперь в программе есть неодушевленная летающая вещь.

Локальные изменения в коде, которые влияют не только на ситуацию (например, летящая резиновая уточка)

Он понял одну вещь: когда дело доходит до «обслуживания», использование наследования для целей повторного использования не заканчивается хорошо.

public class RubberDuck extends Duck {
    @Override
    void display() {
        System.out.println("我是橡皮鸭");
    }
    @Override
    public void quack() {
        System.out.println("修改为吱吱叫");
    }
}

Текущая структура:



Джо вдруг подумал, что если метод fly() будет переопределен, как и метод quack().

@Override
void fly() {
    // 什么事都不做
}

Но возникает другая проблема, тогда в будущем я добавлю деревянную утку, которая не умеет ни летать, ни лаять.

public class DecoyDuck extends Duck {

    @Override
    void display() {
        System.out.println("我是木头鸭");
    }

    @Override
    public void quack() {
        // 什么事都不做
    }

    @Override
    void fly() {
        // 什么事都不做
    }
}

Каждый раз, когда появляется новый подкласс утки, он вынужден проверять и, возможно, переопределять функции fly() и quack(), что является бесконечным кошмаром. . .

Итак, ему нужен более четкий способ заставить определенные типы уток летать или кричать.

Есть идея: вынести fly() из суперкласса, чтобы интерфейс flyable реализовывали только летающие утки, тот же метод можно использовать в quack(), спроектировать интерфейс quackable

public interface Quackable {
    void quack();
}
public interface Flyable {
    void fly();
}

Реализовано как наследование Реализовано пунктирной линией


Что вы думаете об этом дизайне?


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

Мы знаем, что наследование не является подходящим решением, хотя flyable и quackable могут решить некоторые проблемы, они делают код непригодным для использования, о котором можно только сказать, что он перескакивает из одного кошмара в другой.

Затем посмотрите вниз

Неизменная истина разработки программного обеспечения: независимо от того, насколько хорошо разработано программное обеспечение, оно всегда должно расти и меняться с течением времени, иначе оно умрет.


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

Принцип проектирования 1. Найдите места в приложении, которые, возможно, нужно изменить, изолируйте их и не смешивайте с кодом, который не нужно менять. (Это наш первый принцип дизайна, дальше будет больше)

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

Вернемся к нашей проблеме, уберем утиное поведение летать и крякать из класса уток.

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

Помня об этих целях, давайте рассмотрим второй принцип проектирования:

Принцип проектирования 2: Программа для интерфейса, а не для реализации

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

Мы используем интерфейсы для представления каждого поведения, скажем, flyBehavior и quack.Поведение, и каждая реализация поведения реализует один из этих интерфейсов. Итак, на этот раз класс утки не будет реализовывать интерфейсы flyable и quackable, а мы создадим набор других классов для его конкретной реализации.flyBehavior и шарлатанствоПоведение, это называется классом поведения. Интерфейс поведения реализуется классом поведения вместо класса утки.

public interface FlyBehavior {
    void fly();
}

public class FlyCanWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("会飞");
    }
}

public class FlyNotWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("不会飞");
    }
}

public interface QuackBehavior {
    void quack();
}
public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("呱呱叫");
    }
}
public class Squeak implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("吱吱叫");
    }
}

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


Теперь начните интегрировать поведение утки

Вот как:

Во-первых, добавьте две переменные экземпляра в класс duck, а именно flyBehavior и quackBehavior.Каждый объект будет динамически устанавливать эти переменные для ссылки на правильный тип поведения во время выполнения.

Мы находим два похожих метода: PerformFly() и PerformQuack(), которые заменяют fly() и quack() в функции duck. смотреть вниз

@Data
public abstract class Duck {
    /* 鸭子游泳 */
    public void swim(){}
    /* 鸭子形状  可能有很多种类的鸭子,所以display方法是抽象的 */
    abstract void display();

    FlyBehavior flyBehavior;

    QuackBehavior quackBehavior;

    void performFly(){
        // 每只鸭子都会引用实现FlyBehavior接口的对象
        flyBehavior.fly();
    }

    void performQuack(){
        quackBehavior.quack();
    }
}


Теперь давайте позаботимся о том, как установитьflyПоведение ипеременная quackBehavior

public class MallardDuck extends Duck {
    @Override
    void display() {
        System.out.println("我是绿头鸭");
    }
    /**
     *  因为继承了duck 所以拥有这两个变量
     */
    public MallardDuck() {
        quackBehavior = new Quack();
        flyBehavior = new FlyCanWay();
    }
}

Mallard использует класс quack для обработки quack, поэтому при вызове PerformQuack ответственность за вызов делегируется объекту quack, и мы получаем настоящий quack.

Ты понимаешь? когда
При создании экземпляра MallardDuck его конструктор наследуетПеременная экземпляра quackBehavior инициализируется новым экземпляром типа quack,То же самое верно и для flyBehavior.

пройти тест

public static void main(String[] args) {
    Duck mallardDuck = new MallardDuck();
    mallardDuck.performFly();
    mallardDuck.performQuack();
}

результат


Как заставить утку иметь динамическое поведение?

Используя установочный метод flyBehavior, мы можем в любой момент вызвать установщик, чтобы изменить поведение утки.

public void setFlyBehavior(FlyBehavior flyBehavior) {
    this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
    this.quackBehavior = quackBehavior;
}

Присоединяйтесь к нашим новым видам уток

public class ModelDuck extends Duck {

    @Override
    void display() {
        System.out.println("我是一只橡皮鸭");
    }

    public ModelDuck() {
        flyBehavior = new FlyNotWay();
        quackBehavior = new Squeak();
    }
}

На этом этапе мы позволяем fly иметь полет на ракете и обычный полет и реализуем интерфейс flyByhavior.

public class FlyRocket implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("火箭飞");
    }
}

Это только делает модель уткой, которая не может влететь в летящую ракету.

public static void main(String[] args) {
    Duck mallardDuck = new MallardDuck();
    mallardDuck.performFly();
    mallardDuck.performQuack();

    Duck modelDuck = new ModelDuck();
    modelDuck.performFly();
    modelDuck.setFlyBehavior(new FlyRocket());
    modelDuck.performFly();
}

результат:

может летать

квакать

не могу летать

ракета летать

Что ж, мы углубились в дизайн симулятора утки. Ниже приведена схема модели


Другой принцип проектирования возникает, когда вы объединяете наследование и реализацию вместе:

Принцип проектирования 3. Используйте больше композиции (наследование плюс реализация), меньше наследования

На этом статья заканчивается.На самом деле вышеприведенный шаблон является первым шаблоном проектирования: шаблоном стратегии!

Шаблон стратегии: инкапсулируйте их отдельно, чтобы их можно было заменить друг другом.


Суммировать

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

Дальше буду учиться и продолжать писать, даже если никто не читает хахаха