这是 java 中桥接模式的正确实现吗?

Is this a correct implementation of Bridge pattern in java?

我正在努力提高我对设计模式的了解,我对 Bridge 模式有点困惑。下面你可以看到我的例子:

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. 如果将接口替换为 abstract classes(我在这个教程中看到了这种方法 http://www.newthinktank.com/2012/10/bridge-design-pattern-tutorial/)。 但是根据这个 (https://dzone.com/articles/design-patterns-bridge) 好像 就我而言,动物不应该是抽象的 class..
  3. 是否需要方法 someAnimalEats()someAnimalMoves() 在环境 class 中?更准确地说,是不是 必须在此 class 中具有与每个对应的方法 来自 Animal 接口的方法?

非常感谢!

  1. 它具有 Bridge 模式的结构,因为它具有模式中典型的那种 class 关系和方法。我不确定您示例的语义是否适合这种模式。此模式的主要要点是您有一个使用一个或多个 实现 class 的 class。你会说你的 Animal classes 提供 Environment 的实现吗?

  2. 是的,你确实可以使用抽象classes。请记住,设计模式是典型的语言不可知论者。在某些语言中,例如 C++,接口是使用包含纯抽象方法的 classes 创建的,因为与 Java 不同,C++ 中没有用于接口的特定关键字。图中的标签 "interface" 表示概念意义上的接口,而不是实际的 Java 关键字。事实上,您甚至可以使用具体的 class 作为您的实现者,尽管使用接口通常是一种很好的做法,因为它提供了更大的灵活性。

  3. 不,没有必要使用与实现者 classes 完全相同的方法。实现者 classes 提供了许多被其他 class 使用的实现方法。它如何使用它们取决于 class 想要提供的功能。

Bridge 模式在其他语言中更有用,例如 C++,它被称为 "pImpl idiom" 或 "opaque pointer",因为它可以用来对用户隐藏实现:他们不能查看有关实施的任何信息 class。在Java中,这种隐藏是不可能的。

  1. 您展示的领域(动物及其环境)并不是桥接模式的良好用例。它有一个非常具体的目的:将抽象(包括该抽象的扩展)与实现(同样可能包括扩展)分开。关键特征之一是抽象引用实现(名称中的 'bridge')而不是扩展或实现抽象的实现。一般具体实施由客户在运行-时间决定。

很难想到模拟真实世界对象(如动物和环境)的桥梁的自然用例。更容易想到旨在执行某些功能的 class。

// 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. 我建议我在此示例中使用的 class / 接口组合非常典型。您可以进行抽象和接口,但考虑到桥接点是引用实现,因此更容易使其成为抽象 class.

  2. 希望这个例子也能回答这个问题:你可以在抽象和实现中有类似的方法,但这当然不是必需的。该模式有趣且有用的方面之一是不同的独立特征(在本示例中,记录的内容和记录方式)可以单独定义为抽象和实现的扩展。这使您可以混合和匹配特征,而不会出现 class 结构失控的情况。这些特性的独立性(即正交性)往往会要求两种结构中的方法完全不同。