Является ли это правильной реализацией шаблона моста в java?


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

public interface Animal {
    public abstract void eat();
    public abstract void move();
}


public class Jaguar implements Animal{

    @Override
    public void eat() {
        System.out.println("The Jaguar eats meat");
    }

    @Override
    public void move() {
        System.out.println("The Jaguar runs");
    }
 }

public class Kangaroo implements Animal{

    @Override
    public void eat() {
        System.out.println("THe Kangaroo eats grass and other vegetables");
    }

    @Override
    public void move() {
        System.out.println("The Kangaroo hops around :)");
    }

}

public abstract class Environment {

    private Animal animal;

    public Environment(Animal animal){
        this.animal = animal;
    }

    public void someAnimalEats(){
        this.animal.eat();
    }

    public void someAnimalMoves(){
        this.animal.move();
    }

    public abstract void environmentPolutionStatus();
}


public class Australia extends Environment{

    public Australia(Animal animal) {
        super(animal);
    }

    @Override
    public void environmentPolutionStatus() {
        System.out.println("Australia is in good shape as far as polution is concerned.");
    }

}


public class Africa extends Environment{

    public Africa(Animal animal) {
        super(animal);
    }

    @Override
    public void environmentPolutionStatus() {
        System.out.println("Africa looks pretty good, however the hunting is kind of bad");

    }

}

public class BridgePatternMain {
    public static void main(String[] args){
        Environment australiaKangaroo = new Australia(new Kangaroo());
        Environment australiaJaguar = new Australia(new Jaguar());
        Environment africaKangaroo = new Africa(new Kangaroo());
        Environment africaJaguar = new Africa(new Jaguar());

        australiaKangaroo.environmentPolutionStatus();
        australiaKangaroo.someAnimalEats();
        australiaKangaroo.someAnimalMoves();

        australiaJaguar.environmentPolutionStatus();
        australiaJaguar.someAnimalEats();
        australiaJaguar.someAnimalMoves();

        africaKangaroo.environmentPolutionStatus();
        africaKangaroo.someAnimalEats();
        africaKangaroo.someAnimalMoves();

        africaJaguar.environmentPolutionStatus();
        africaJaguar.someAnimalEats();
        africaJaguar.someAnimalMoves();
    }
}

Мои вопросы:

  1. является ли это правильной моделью моста?
  2. возможно ли иметь шаблон моста, если интерфейсы заменены на абстрактные классы (я видел этот подход в этом tutoarial http://www.newthinktank.com/2012/10/bridge-design-pattern-tutorial/). Но в соответствии с этим один (https://dzone.com/articles/design-patterns-bridge ) похоже, что Животное в моем случае не должно быть абстрактным классом..
  3. необходимо ли иметь методы someAnimalEats() и someAnimalMoves() в классе Environment? Точнее, так ли это обязательно иметь в этом классе методы, соответствующие каждому из методы из интерфейса животного?

Большое Спасибо!

2 3

2 ответа:

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

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

// abstraction 
abstract class Logger {
    protected final LogOutputter outputter;
    public abstract void log(String message);
}

// abstraction extension
class ErrorLogger extends Logger {
    public void log(String message) {
        outputter.output("Error: " + message);
    }
}

// implementation interface
interface LogOutputter {
    void output(String message);
}

// implementation extensions
class FileLogOutputter implements LogOutputter ...
class ConsoleLogOutputter implements LogOutputter ...

И клиент может сделать что-то вроде:

Logger logger = new ErrorLogger(new FileLogOutputter("errors.log"));
  1. Я бы предположил, что комбинации класса / интерфейса, которые я использовал в этом примере, довольно типичны. Вы могли бы сделать абстракцию и интерфейс но, учитывая, что смысл моста заключается в ссылке на реализацию, его проще сделать абстрактным классом.

  2. Надеюсь, что пример также отвечает на это: вы могли бы иметь аналогичные методы в абстракции и реализации, но это, конечно, не обязательно. Одним из интересных и полезных аспектов паттерна является то, что различные независимые характеристики (в данном примере, что регистрируется и как это регистрируется) могут быть отдельно определены как расширения абстракции и реализация. Это позволяет смешивать и сопоставлять характеристики, не имея структуры класса, которая выходит из-под контроля. Независимость этих характеристик (то есть ортогональность) часто требует, чтобы методы в этих двух структурах были совершенно разными.

  1. Он имеет структуру шаблона моста, в том смысле, что он имеет тип отношений классов и методов, которые типичны в шаблоне. Я не уверен, что семантика вашего примера хорошо подходит для этой модели. Основная суть этого шаблона заключается в том, что у вас есть класс, который использует один или несколько классов реализации. Вы бы сказали, что ваши классы Animal обеспечивают реализацию для Environment?

  2. Да, вы действительно можете использовать абстрактные классы. Помните, дизайн паттерны типичны для языка-агностика. В некоторых языках, таких как C++, интерфейсы создаются с использованием классов, содержащих чисто абстрактные методы, поскольку в отличие от Java в C++нет специального ключевого слова для интерфейса. Метка "интерфейс" на диаграммах обозначает интерфейс в концептуальном смысле, а не фактическое ключевое слово Java. Фактически, вы даже можете использовать конкретный класс в качестве своего реализатора, хотя обычно рекомендуется использовать интерфейс, так как это обеспечивает больше гибкость.

  3. Нет, не обязательно иметь методы, которые точно отражают методы классов-реализаторов. Классы-исполнители предоставляют ряд методов реализации, которые используются другим классом. То, как он их использует, зависит от того, какие функции хочет предоставить класс.

Шаблон моста более полезен в других языках, таких как C++, где он известен как "идиома pImpl" или "непрозрачный указатель", потому что он может использоваться для скрытия реализации от пользователей: они ничего не видят о реализующем классе. На Яве такой способ сокрытия невозможен.