系统化的学习技术、研究技术而不是使用技术

23种设计模式

2018/11/21 16:58:43 | 浏览(16) Java技术

一、概念

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。 使用设计模式是为了提高代码的可重用性,让代码更容易被他人理解、保证代码的可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

二、设计原则

  1. 开闭原则(Open Closed Principle,OCP)对扩展开放,对修改封闭。
  2. 里氏代换原则(Liskov Substitution Principle,LSP)凡是基类适用的地方,子类一定适用。
  3. 依赖倒置原则(Dependency Inversion Principle,DIP)要依赖抽象,不要依赖具体。
  4. 最小知识原则(Principle of Least Knowledge,PLK)也叫迪米特原则,一个对象应该被其他对象有尽可能少的了解。
  5. 接口隔离原则(Interface Segregation Principle,ISP)使用多个专门的接口比使用单一的接口更好。
  6. 单一职责原则(Single Responsibility Principle,SRP)一个类应该只有一个理由实现单一职责的变更。

三、分类

总体来讲设计模式分为三大类:

  • 创建型模式(5) 单例模式、抽象工厂模式、工厂模式、建造者模式、原型模式。
  • 结构型模式(7) 适配器模式、装饰模式、代理模式、门面模式、桥接模式、组合模式、享元模式。
  • 行为型模式(11) 策略模式、模板方法模式、观察者模式、责任链模式、迭代器模式、命令模式、访问者模式、中介者模式、解释器模式、状态模式、备忘录模式。

I、创建型模式

1、单例模式(Singleton Pattern)#

概念:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

适用场景:

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

举例:

public final class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton(){}
    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

2、抽象工厂模式(Abstract Factory Pattern)

概念:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。

适用场景:

  • 一个系统要独立于它的产品的创建、组合和表示时;
  • 一个系统要由多个产品系列中的一个来配置时;
  • 当你要强调一系列相关的产品对象的设计以便进行联合使用时;
  • 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

举例:

public interface IButton {
    void paint();
}

public interface IGUIFactory {
    public IButton createButton();
}

public class WinFactory implements IGUIFactory {
    @Override
    public IButton createButton() {
        return new WinButton();
    }
}

public class OSXFactory implements IGUIFactory {
    @Override
    public IButton createButton() {
        return new OSXButton();
    }
}

public class WinButton implements IButton {
    @Override
    public void paint() {
        System.out.println("WinButton");
    }
}

public class OSXButton implements IButton {
    @Override
    public void paint() {
        System.out.println("OSXButton");
    }
}

3、工厂模式(Factory Pattern)

概念:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

适用场景:

  • 当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候。
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是 代理者这一信息局部化的时候。

举例:

public abstract class Room {
   abstract void connect(Room room);
}

public class MagicRoom extends Room {
   public void connect(Room room) {}
}

public class OrdinaryRoom extends Room {
   public void connect(Room room) {}
}

public abstract class MazeGame {
    private final List<Room> rooms = new ArrayList<>();
    public MazeGame() {
        Room room1 = makeRoom();
        Room room2 = makeRoom();
        room1.connect(room2);
        rooms.add(room1);
        rooms.add(room2);
    }
    abstract protected Room makeRoom();
}

public class MagicMazeGame extends MazeGame {
    @Override
    protected Room makeRoom() {
        return new MagicRoom(); 
    }
}

public class OrdinaryMazeGame extends MazeGame {
    @Override
    protected Room makeRoom() {
        return new OrdinaryRoom(); 
    }
}

4、建造者模式(Builder Pattern)

概念:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

适用场景:

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
  • 当构造过程必须允许被构造的对象有不同的表示时。

举例:

class Car {
    private int wheels;
    private String color;
    public Car() {}
    // ignore getter and setter
}

interface CarBuilder {
    Car build();
    CarBuilder setColor(final String color);
    CarBuilder setWheels(final int wheels);
}

class CarBuilderImpl implements CarBuilder {
    private Car car;
    public CarBuilderImpl() {
        car = new Car();
    }
    @Override
    public Car build() {
        return car;
    }
    @Override
    public CarBuilder setColor(final String color) {
        car.setColor(color);
        return this;
    }
    @Override
    public CarBuilder setWheels(final int wheels) {
        car.setWheels(wheels);
        return this;
    }
}

public class CarBuildDirector {
    private CarBuilder builder;
    public CarBuildDirector(final CarBuilder builder) {
        this.builder = builder;
    }
    public Car construct() {
        return builder.setWheels(4).setColor("Red").build();
    }
    public static void main(final String[] arguments) {
        final CarBuilder builder = new CarBuilderImpl();
        final CarBuildDirector carBuildDirector = new CarBuildDirector(builder);
        System.out.println(carBuildDirector.construct());
    }
}

5、原型模式(Prototype Pattern)

概念:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

适用场景:

  • 当要实例化的类是在运行时刻指定时,例如,通过动态装载;
  • 为了避免创建一个与产品类层次平行的工厂类层次时;
  • 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

举例:

public abstract class Prototype implements Cloneable {
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }
}

public class ConcretePrototype1 extends Prototype {
    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (ConcretePrototype1)super.clone();
    }
}

public class ConcretePrototype2 extends Prototype {
    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (ConcretePrototype2)super.clone();
    }
}

II、结构型模式

6、适配器模式(Adpater Pattern)

概念:将一个类的接口转换成另外一个客户希望的接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适用场景:

  • 你想使用一个已经存在的类,而它的接口不符合你的需求。
  • 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
  • (仅适用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

示例:

interface IFormatIPhone {
    void connectByLighting();
    void recharge();
}

interface IFormatAndroid {
    void connectByMicroUsb();
    void recharge();
}

class IPhone implements IFormatIPhone {
    private boolean isConnected;
    public void connectByLighting() {
        this.isConnected = true;
    }
    public void recharge() {
        if (this.isConnected) {
            System.out.println("IPhone Is Recharging...");
        }
    }
}

class Android implements IFormatAndroid {
    private boolean isConnected;
    public void connectByMicroUsb() {
        this.isConnected = true;
    }
    public void recharge() {
        if (this.isConnected) {
            System.out.println("Android Is Recharging...");
        }
    }
}

class IPhoneAdapter implements IFormatAndroid {
    private IPhone phone;
    public IPhoneAdapter(IPhone phone) {
        this.phone = phone;
    }
    public void connectByMicroUsb() {
        phone.connectByLighting();
    }
    public void recharge() {
        phone.recharge();
    }
}

class MicroUsbRecharger {
    public static void recharge() {
        IPhone phone = new IPhone();
        IPhoneAdapter adapter = new IPhoneAdapter(phone);
        adapter.connectByMicroUsb();
        adapter.recharge();
    }
}

7、装饰模式(Decorator Pattern)

概念:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。

适用场景:

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 处理那些可以撤消的职责。
  • 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

举例:

interface ICoffee {
    double getCost();
    String getIngredients();
}

class SimpleCoffee implements ICoffee {
    public double getCost() {
        return 1;
    }
    public String getIngredients() {
        return "Coffee";
    }
}

abstract class CoffeeDecorator implements ICoffee {
    protected SimpleCoffee decoratedCoffee;
    public CoffeeDecorator(ICoffee coffee) {
        this.decoratedCoffee = coffee;
    }
    public double getCost() {
        return decoratedCoffee.getCost();
    }
    public String getIngredients() {
        return decoratedCoffee.getIngredients();
    }
}

class WithMilk extends CoffeeDecorator {
    public WithMilk(ICoffee c) {
        super(c);
    }
    public double getCost() {
        return super.getCost() + 0.5;
    }
    public String getIngredients() {
        return super.getIngredients() + ", Milk";
    }
}

class WithSprinkles extends CoffeeDecorator {
    public WithSprinkles(ICoffee c) {
        super(c);
    }
    public double getCost() {
        return super.getCost() + 0.2;
    }
    public String getIngredients() {
        return super.getIngredients() + ", Sprinkles";
    }
}

class Main {
    public static void main(String[] args) {
        ICoffee c = new SimpleCoffee();
        c = new WithMilk(c);
        c = new WithSprinkles(c);
    }
}

8、代理模式(Proxy Pattern)

概念:为其他对象提供一种代理以控制对这个对象的访问。

适用场景:

在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一 些可以使用Proxy模式常见情况:

  • 远程代理(Remote Proxy)为一个对象在不同的地址空间提供局部代表。
  • 虚代理(Virtual Proxy)根据需要创建开销很大的对象。
  • 保护代理(Protection Proxy)控制对原始对象的访问。保护代理用于对象应该有不同 的访问权限的时候。
  • 智能指引(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。 它的典型用途包括:
    • 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为SmartPointers)。
    • 当第一次引用一个持久对象时,将它装入内存。
    • 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

动态代理:

  1. 根据被代理对象的接口生成所有方法,比如JDK提供的动态代理。
  2. 继承被代理对象,生成所有公有方法,比如CGLIB。

举例:

interface IService {
    void doSomething();
}

class Service implements IService {

    public void doSomething() {
        System.out.println("Hello, I am Service");
    }
}

class ProxyService implements IService {
    private IService realService;

    public void doSomething() {
        System.out.println("begin...");
        realService.doSomething();
        System.out.println("end...");
    }
}

9、门面模式(Facade Pattern)

概念:为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

适用场景:

  • 为一个复杂的模块或子系统提供一个供外界访问的接口。
  • 子系统相对独立——外界对子系统的访问只要黑箱操作即可。
  • 预防低水平人员带来的风险扩散。

注意:

  • 一个子系统可以有多个门面
  • 门面不参与子系统内的业务逻辑

举例:

class CPU {
    public void freeze() { ... }
    public void jump(long position) { ... }
    public void execute() { ... }
}

class HardDrive {
    public byte[] read(long lba, int size) { ... }
}

class Memory {
    public void load(long position, byte[] data) { ... }
}

class ComputerFacade {
    private CPU processor;
    private Memory ram;
    private HardDrive hd;
    public ComputerFacade() {
        this.processor = new CPU();
        this.ram = new Memory();
        this.hd = new HardDrive();
    }
    public void start() {
        processor.freeze();
        ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE));
        processor.jump(BOOT_ADDRESS);
        processor.execute();
    }
}

class You {
    public static void main(String[] args) {
        ComputerFacade computer = new ComputerFacade();
        computer.start();
    }
}

10、桥接模式(Bridge Pattern)

概念:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

适用场景:

  • 不希望或不适用使用继承的场景。
  • 接口或抽象类不稳定的场景。
  • 重用性要求较高的场景。

举例:

/** "Implementor" */
interface DrawingAPI {
    public void drawCircle(final double x, final double y, final double radius);
}

/** "ConcreteImplementor"  1/2 */
class DrawingAPI1 implements DrawingAPI {
    public void drawCircle(final double x, final double y, final double radius) {
        System.out.printf("API1.circle at %f:%f radius %f%n", x, y, radius);
    }
}

/** "ConcreteImplementor" 2/2 */
class DrawingAPI2 implements DrawingAPI {
    public void drawCircle(final double x, final double y, final double radius) {
        System.out.printf("API2.circle at %f:%f radius %f%n", x, y, radius);
    }
}

/** "Abstraction" */
abstract class Shape {
    protected DrawingAPI drawingAPI;
    protected Shape(final DrawingAPI drawingAPI){
        this.drawingAPI = drawingAPI;
    }
    public abstract void draw();                                 // low-level
    public abstract void resizeByPercentage(final double pct);   // high-level
}

/** "Refined Abstraction" */
class CircleShape extends Shape {
    private double x, y, radius;
    public CircleShape(final double x, final double y, final double radius, final DrawingAPI drawingAPI) {
        super(drawingAPI);
        this.x = x;  this.y = y;  this.radius = radius;
    }

    // low-level i.e. Implementation specific
    public void draw() {
        drawingAPI.drawCircle(x, y, radius);
    }
    // high-level i.e. Abstraction specific
    public void resizeByPercentage(final double pct) {
        radius *= (1.0 + pct/100.0);
    }
}

/** "Client" */
class BridgePattern {
    public static void main(final String[] args) {
        Shape[] shapes = new Shape[] {
            new CircleShape(1, 2, 3, new DrawingAPI1()),
            new CircleShape(5, 7, 11, new DrawingAPI2())
        };

        for (Shape shape : shapes) {
            shape.resizeByPercentage(2.5);
            shape.draw();
        }
    }
}

11、组合模式(Composite Pattern)

概念:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

适用场景:

  • 你想表示对象的部分—整体层次结构。
  • 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

举例:

class Composite extends Component {
    private List<Component> components = new ArrayList<Component>();
    public void add(Component component){
        this.components.add(component);
    }
    public void remove(Component component) {
        this.components.remove(component);
    }
    public ArrayList<Component> getChildren() {
        return this.components;
    }
}

12、享元模式(Flyweight Pattern)

概念:运用共享技术有效地支持大量细粒度的对象。

适用场景:

  • 一个应用程序使用了大量的对象。
  • 完全由于使用大量的对象,造成很大的存储开销。
  • 对象的大多数状态都可变为外部状态。
  • 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
  • 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。

举例:

不理解享元模式,查看Wiki的解释

III、行为型模式

13、策略模式(Strategy Pattern)

概念:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

适用场景:

  • 多个类只有在算法或行为上稍有不同的场景。
  • 算法需要自由切换的场景。
  • 需要屏蔽算法规则的场景。

注意:具体策略数量超过4个,则需要考虑使用混合模式

举例:

interface IStrategy {
    double getDiscount();
}

class NormalStrategy implements IStrategy {
    public double getDiscount() {
        return 1;
    }
}

class PreferentialStrategy implements IStrategy {
    public double getDiscount() {
        return 0.8;
    }
}

class StrategyFactory {
    public static IStrategy getStrategy(CustomerLevel level) {
        switch(level) {
            CustomerLevel.VIP: return new PreferentialStrategy();
            default: return new NormalStrategy();
        }
    }
}

14、模板方法模式(Template Method Pattern)

概念:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

适用场景:

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke和Johnson所描述过的“重分解以一般化”的一个很好的例子。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
  • 控制子类扩展。模板方法只在特定点调用“hook”操作,这样就只允许在这些点进行扩展。

举例:

abstract class Biz {
    public final void run() {
        preprocess();
        doRun();
        postprocess();
    }
    protected void preprocess() {}
    protected void postprocess() {}
    protected abstract void doRun();
}

15、观察者模式(Observer Pattern)

概念:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

适用场景:

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  • 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。

举例:

interface Observer {
    void update();
}

abstract class Subject {
    private List<Observer> list = new ArrayList<>();
    public void add(Observer o) {
        list.add(o);
    }
    public void remove(Observer o) {
        list.remove(o);
    }
    public void notifyObserver() {
        for (Observer o : list) {
            o.update();
        }
    }
}

16、责任链模式(Chain-of-responsibility Pattern)

概念:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

适用场景:

  • 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
  • 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  • 可处理一个请求的对象集合应被动态指定。

抽象的处理者实现三个职责:

  1. 定义一个请求的处理方法handleMessage,唯一对外开放的方法;
  2. 定义一个链的编排方法setNext,设置下一个处理者;
  3. 定义具体的请求者必须实现的两个方法:
    • 自己能够处理的级别getHandlerLevel
    • 具体的处理任务

举例:

abstract class Handler {
    private Handler nextHandler;
    public final Response handleMessage(Request request) {
        Response response = null;
        if (this.getHandlerLevel().equals(request.getRequestLevel())) {
            response = this.echo(request); // 判断是否是自己的处理级别
        }
        else if (this.nextHandler != null) { // 判断是否有下一个处理者
            response = this.nextHandler.handleMessage(request);
        }
        return response;
    }
    public void setNext(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }
    protected abstract Level getHandlerLevel();
    protected abstract Response echo(Request request);
}

17、迭代器模式(Iterator Pattern)

概念:提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

适用场景:

  • 访问一个聚合对象的内容而无需暴露它的内部表示。
  • 支持对聚合对象的多种遍历。
  • 为遍历不同的聚合结构提供一个统一的接口(即支持多态迭代)。

举例:

class RedHead<T> implements Iterable<T> {
    private Set<T> redHeads = new HashSet<>();
    public void add(final T redHead) {
        redHeads.add(redHead);
    }
    @Override
    public Iterator<T> iterator() {
        return redHeads.iterator();
    }
}

class Weasley {
    private String name;
    public Weasley(final String name) {
        this.name = name;
    }
    public String toString() {
        return this.name + " Weasley";
    }
}

class MainTest {
    public static void main(String[] args) {
        RedHead<Weasley> redHead = new RedHead<>();
        redHead.add(new Weasley("Arthur"));
        redHead.add(new Weasley("Molly"));
        redHead.add(new Weasley("Bill"));
        for (Weasley rh : redHead) {
            System.out.println(rh);
        }
    }
}

18、命令模式(Command Pattern)

概念:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。

适用场景:

举例:

@FunctionalInterface
interface Command {
    void apply();
}

class CommandFactory {
    private final Map<String, Command> commands = new HashMap<>();
    public void addCommand(String name, Command command) {
        commands.put(name, command);
    }
    public void executeCommand(String name) {
        if (commands.containsKey(name)) {
            commands.get(name).apply();
        }
    }
    public void listCommands() {
        System.out.println(commands.keySet().stream().collect(Collectors.joining(", ")));
    }
    public static CommandFactory init() {
        CommandFactory cf = new CommandFactory();
        cf.addCommand("Light on", () -> System.out.println("Light turned on"));
        cf.addCommand("Light off", () -> System.out.println("Light turned off"));
        return cf;
    }
}

class MainTest {
    public static void main(String[] args) {
        final CommandFactory cf = CommandFactory.init();
        cf.executeCommand("Light on");
        cf.listCommands();
    }
}

19、访问者模式(Visitor Pattern)

概念:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

适用场景:

  • 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
  • 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。

举例:

interface CarElementVisitor {
    void visit(Car car);
    void visit(Engine engine);
}

interface CarElement {
    void accept(CarElementVisitor visitor);
}

class Car implements CarElement {
    CarElement[] elements;
    public Car() {
        this.elements = new CarElement[] {new Engine()};
    }
    public void accept(CarElementVisitor visitor) {
        for (CarElement element : elements) {
            element.accept(visitor);
        }
        visitor.visit(this);
    }
}

class Engine implements CarElement {
    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}

class CarElementDoVisitor implements CarElementVisitor {
    public void visit(Car car) {
        System.out.println("Starting my car");
    }
    public void visit(Engine engine) {
        System.out.println("Starting my engine");
    }
}

class MainTest {
    public static void main(final String[] args) {
        Car car = new Car();
        car.accept(new CarElementDoVisitor());
    }
}

20、中介者模式(Mediator Pattern)

概念:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

适用场景:

  • 一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
  • 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
  • 想定制一个分布在多个类中的行为,而又不想生成太多的子类。

举例:

interface Command {
    void execute();
}

interface Mediator {
    void view();
    void search();
    void registerView(BtnView v);
    void registerSearch(BtnSearch s);
}

class ParticipantMediator implements Mediator {
    BtnView btnView;
    BtnSearch btnSearch;
    public void registerView(BtnView v) {btnView = v;}
    public void registerSearch(BtnSearch s) {btnSearch = s;}
    public void view() {
        btnView.setEnabled(false);
        btnSearch.setEnabled(true);
        show.setText("viewing...");
    }
    public void search() {
        btnSearch.setEnabled(false);
        btnView.setEnabled(true);
        show.setText("searching...");
    }
}

class BtnView extends JButton implements Command {
    Mediator med;
    BtnView(ActionListener al, Mediator m) {
        super("View");
        addActionListener(al);
        med = m;
        med.registerView(this);
    }
    public void execute() {med.view();}
}

class BtnSearch extends JButton implements Command {
    Mediator med;
    BtnSearch(ActionListener al, Mediator m) {
        super("Search");
        addActionListener(al);
        med = m;
        med.registerSearch(this);
    }
    public void execute() {med.search();}
}

class MediatorDemo extends JFrame implements ActionListener {
    Mediator med = new ParticipantMediator();
    MediatorDemo() {
        JPanel p = new JPanel();
        p.add(new BtnView(this, med));
        p.add(new BtnSearch(this, med));
        getContentPane().add(p, "South");
        setSize(400, 200);
        setVisible(true);
    }
    public void actionPerformed(ActionEvent ae) {
        Command comd = (Command) ae.getSource();
        comd.execute();
    }
    public static void main(String[] args) {
        new MediatorDemo();
    }
}

21、解释器模式(Interpreter Pattern)

概念:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

适用场景:

  • 重复发生的问题可以使用解释器模式。
  • 一个简单语法需要解释的场景。
  • 比较少用。

举例:

参见 Interpreter_pattern

22、状态模式(State Pattern)

概念:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

适用场景:

  • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
  • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

注意:

状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。

举例:

interface IStatelike {
    void writeName(StateContext context, String name);
}

class StateLowerCase implements IStatelike {
    @Override
    public void writeName(StateContext context, String name) {
        System.out.println(name.toLowerCase());
        context.setState(new StateMultipleUpperCase());
    }
}

class StateMultipleUpperCase implements IStatelike {
    private int count = 0;
    @Override
    public void writeName(StateContext context, String name) {
        System.out.println(name.toUpperCase());
        if(++count > 1) {
            context.setState(new StateLowerCase());
        }
    }
}

class StateContext {
    private IStatelike myState;
    StateContext() {
        setState(new StateLowerCase());
    }
    void setState(IStatelike newState) {
        myState = newState;
    }
    public void writeName(String name) {
        myState.writeName(this, name);
    }
}

class MainTest {
    public static void main(String[] args) {
        final StateContext sc = new StateContext();
        sc.writeName("Monday");
        sc.writeName("Tuesday");
        sc.writeName("Wednesday");
    }
}

23、备忘录模式(Memento Pattern)

概念:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。

适用场景:

  • 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
  • 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

举例:

class Originator {
    private String state;
    public void set(String state) {
        this.state = state;
        System.out.println("Originator: Setting state to " + state);
    }
    public Memento saveToMemento() {
        System.out.println("Originator: Saving to Memento.");
        return new Memento(this.state);
    }
    public void restoreFromMemento(Memento memento) {
        this.state = memento.getSavedState();
        System.out.println("Originator: State after restoring from Memento: " + state);
    }
    public static class Memento {
        private final String state;
        public Memento(String stateToSave) {
            state = stateToSave;
        }
        private String getSavedState() {
            return state;
        }
    }
}

class Caretaker {
    public static void main(String[] args) {
        List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();

        Originator originator = new Originator();
        originator.set("State1");
        originator.set("State2");
        savedStates.add(originator.saveToMemento());
        originator.set("State3");
        savedStates.add(originator.saveToMemento());
        originator.set("State4");

        originator.restoreFromMemento(savedStates.get(1));   
    }
}

参考资料

留言(0)

给我留言