设计模式二:结构型设计模式与关系型设计模式

这是设计模式的第二篇笔记,记录了结构性设计模式与关系型设计模式的内容,不包括全部,只有部分常用的设计模式。
如有错误,欢迎指正!

适配器模式

适配器模式:将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作。
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承关系。在实际开发中,对象适配器的使用频率更高,对象适配器的结构如下:

适配器模式中包含的角色:
Target(目标抽象类):目标抽象类定义客户所需要的接口;
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象试二者产生联系;
Adaptee(适配者类):适配者类为被适配的角色,它定义了一个已经存在的接口,这个接口需要适配。
对象适配器模式中,适配器拥有适配者类的对象,可以直接调用适配者类的方法,达到适配的目的。

class Adapter extends Target {
    private Adaptee adaptee; //维持一个对适配者对象的引用
    public Adapter(Adaptee adaptee) {
    this.adaptee=adaptee;
    }
    public void request() {
        adaptee.specificRequest(); //转发调用
    }
}

外观模式

外观模式:为子系统中的一组接口提供一个统一的入口。

外观模式包含如下的两个角色:
Facade(外观角色):在客户端中可以调用它的方法,在外观角色中可以知道相关的子系统的功能和职责;
SubSystem(子系统):在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个类,而是一个类的集合;每一个子系统都可以被客户端直接调用,或者被外观角色直接调用;子系统并不知道外观的存在,对于子系统而言,外观角色也是一个客户端。
在标准的外观模式中并没有抽象外观类,但如何涉及到增加、删除、更换子系统类的操作,那么就会违背开闭原则,如果涉及到这些功能,可以增加抽象外观类,通过抽象外观类进行调用,根据客户端的需求实例化不同的子系统,以符合开闭原则。

代理模式

代理模式:给某一个对象提供一个代理或者占位符,并由代理对象来控制对原对象的访问。
代理模式的核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层,代理模式结构图如下:

代理模式中涉及到的角色:
Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以用代理主题,客户端只需要针对抽象主题角色进行编程;
Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;
RealSubject(真实主题角色)它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

//抽象主题代码
abstract class Subject {
    public abstract void Request();
}
...
//真实主题代码
class RealSubject : Subject{
    public override void Request(){
        //业务方法具体实现代码
    }
}
...
//代理主题
class Proxy : Subject{
    private RealSubject realSubject = new RealSubject(); //维持一个对真实主题对象的引用
    public void PreRequest(){
        …...
    }
    public override void Request(){
        PreRequest();
        realSubject.Request(); //调用真实主题对象的方法
        PostRequest();
    }
    public void PostRequest(){
        ……
    }
}

在实际开发过程中,代理类的实现要比上述代码复杂很多,代理模式根据目的和实现方式不同可以分为很多种类:

  1. 远程代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可以是在另一台主机中,远程代理又称为大使;
  2. 虚拟代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只有在需要的时候才会被真正创建;
  3. 保护代理:控制对一个对象的访问,可以给不同的用户提供不同级别的访问权限;
  4. 缓冲代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果;
  5. 智能引用代理:当一个对象被引用时,提供一些额外的操作,例如将对象的被调用次数记录下来等。

命令模式

命令模式:将一个请求封装为一个对象,从而让我们可以用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式提供了一种降低请求发送者和接受者之间耦合度的解决方案。
命令模式的核心在于引入了命令类,通过命令类来降低发送者和接受者的耦合度,请求发送只制定一个命令对象,再通过命令对象来调用请求接受者的处理方式,结构如下:

在命令模式中包含的角色:
Command(抽象命令类):抽象命令类一般是一个抽象类或者接口,在其中生命了用于执行请求的execute等方法,通过这些方法可以调用请求接受者的相关操作;
ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接受者对象,将接受者对象的动作绑定在其中;
Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求;
Receiver(接受者):接受者执行与请求相关的操作,它具体实现对请求的业务处理。
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行相应的操作。命令模式允许请求的一方和接收的一方独立开来,得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。

// 抽象命令类
abstract class Command {
    public abstract void execute();
}
//调用者
class Invoker {
    private Command command;
    //构造注入
    public Invoker(Command command) {
        this.command = command;
    }
    //设值注入
    public void setCommand(Command command) {
        this.command = command;
    }
    //业务方法,用于调用命令类的execute()方法
    public void call() {
        command.execute();
    }
}
//具体命令类
class ConcreteCommand extends Command {
    private Receiver receiver; //维持一个对请求接收者对象的引用
    public void execute() {
        receiver.action(); //调用请求接收者的业务处理方法action()
    }
}
//接收者
class Receiver {
    public void action() {
        //具体操作
    }
}

迭代器模式

迭代器模式:提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标。
在迭代器模式结构中包含聚合和迭代器两个层次结构,考虑到系统的灵活性和可扩展性,在迭代器模式中应用了工厂方法模式,其模式结构如下:

在迭代器模式结构中包含如下角色:
Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法;
ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成了对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置;
Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个createIterator方法用于创建迭代器对象,充当抽象迭代器工厂角色;
ConcreteAggregate(具体聚合类):它实现了抽象聚合类中声明的createIterator方法,该方法返回一个ConcreteIterator对象。
在迭代器模式中,提供了一个外部的迭代器来对聚合对象进行访问和遍历,迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过而哪些没有。

//抽象迭代器,声明用于遍历聚合对象中存储元素的方法
interface Iterator {
    public void first(); //将游标指向第一个元素
    public void next(); //将游标指向下一个元素
    public boolean hasNext(); //判断是否存在下一个元素
    public Object currentItem(); //获取游标指向的当前元素
}
//具体迭代器,实现抽象迭代器中的方法
class ConcreteIterator implements Iterator {
    private ConcreteAggregate objects; //维持一个对具体聚合对象的引用,以便于访问存储在聚合对象中的数据
    private int cursor; //定义一个游标,用于记录当前访问位置
    public ConcreteIterator(ConcreteAggregate objects) {
        this.objects=objects;
    }
    public void first() { ...... }
    public void next() { ...... }
    public boolean hasNext() { ...... }
    public Object currentItem() { ...... }
}
//抽象聚合类
interface Aggregate {
Iterator createIterator();
}
//具体聚合类,一方面负责存储数据,一方面实现在抽象聚合类中声明的工厂方法createIterator,创建对应的迭代器对象
class ConcreteAggregate implements Aggregate {
......
    public Iterator createIterator() {
        return new ConcreteIterator(this);
    }
......
}

观察者模式

观察者模式:定义对象之间的一种一对多的依赖关系,使得当一个对象状态发生变化时,其相关依赖对象都会得到通知并被自动更新。
观察者模式结构中通常包含观察目标和观察者两个继承层次结构,结构如下:

在观察者模式结构图中包含如下角色:
Subject(目标):目标又称为主题,是被观察的对象。在目标中定义一个观察者集合,一个观察目标可以有多个观察者来观察,它提供一系列方法增加和删除观察者对象,同时定义了通知方法notify;
ConcreteSubject(具体目标):具体目标是目标的子类,通常包含有经常发生改变的数据,当它的状态改变时,向各个观察者发出通知;
Observe(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update;
ConcreteObserve(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了Observe中的抽象update方法。
观察者模式描述了如何建立对象与对象之间的依赖关系,以及如何构造满足这种需求的系统。观察者模式包含观察目标和观察者两类对象,一个目标可以有任意数目的与之相依赖的观察者,一旦观察目标的状态发生改变,所有的观察者都将得到通知。作为对这个通知的响应,每个观察者都将监视观察目标的状态以使其状态与目标状态同步,这种交互也称为发布-订阅(Publish-Subscribe)。

//抽象目标类
abstract class Subject {
    //定义一个观察者集合用于存储所有观察者对象
    protected ArrayList observers = new ArrayList();
    //注册方法,用于向观察者集合中增加一个观察者
    public void attach(Observer observer) {
        observers.add(observer);
    }
    //注销方法,用于在观察者集合中删除一个观察者
    public void detach(Observer observer) {
        observers.remove(observer);
    }
    //声明抽象通知方法
    public abstract void notify();
}
//具体目标
class ConcreteSubject extends Subject {
    //实现通知方法
    public void notify() {
        //遍历观察者集合,调用每一个观察者的响应方法
        for(Object obs:observers) {
        ((Observer)obs).update();
        }
    }
}
//抽象观察者
interface Observer {
    //声明响应方法
    public void update();
}
//具体观察者
class ConcreteObserver implements Observer {
    //实现响应方法
    public void update() {
         //具体响应代码
    }
}

状态模式

状态模式:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心,其结构如图所示:

在状态模式结构图中包含如下几个角色:
Context(环境类):环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。
State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。
ConcreteState(具体状态类):它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。
在状态模式中,我们将对象在不同状态下的行为封装到不同的状态类中,为了让系统具有更好的灵活性和可扩展性,同时对各状态下的共有行为进行封装,我们需要对状态进行抽象,引入了抽象状态类角色,其典型代码如下所示:

//抽象状态类
abstract class State {
    //声明抽象业务方法,不同的具体状态类可以不同的实现
    public abstract void handle();
}
//具体状态类
class ConcreteState extends State {
    public void handle() {
    //方法具体实现代码
    }
}
//环境类
class Context {
    private State state; //维持一个对抽象状态对象的引用
    private int value; //其他属性值,该属性值的变化可能会导致对象状态发生变化
    //设置状态对象
    public void setState(State state) {
        this.state = state;
    }
    public void request() {
        //其他代码
        state.handle(); //调用状态对象的业务方法
        //其他代码
    }
}

环境类实际上是真正拥有状态的对象,我们只是将环境类中与状态有关的代码提取出来封装到专门的状态类中。在状态模式结构图中,环境类Context与抽象状态类State之间存在单向关联关系,在Context中定义了一个State对象。在实际使用时,它们之间可能存在更为复杂的关系,State与Context之间可能也存在依赖或者关联关系。

策略模式

策略模式:定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式。
策略模式结构并不复杂,但我们需要理解其中环境类Context的作用,其结构如图所示:
72a5c38b35533c24ae8b9f962a91d782.png
在策略模式结构图中包含如下几个角色:
Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。
策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列具体策略类里面,作为抽象策略类的子类。在策略模式中,对环境类和抽象策略类的理解非常重要,环境类是需要使用算法的类。在一个系统中可以存在多个环境类,它们可能需要重用一些相同的算法。
在使用策略模式时,我们需要将算法从Context类中提取出来,首先应该创建一个抽象策略类,其典型代码如下所示:

abstract class AbstractStrategy {
    public abstract void algorithm(); //声明抽象算法
}
class ConcreteStrategyA extends AbstractStrategy {
    //算法的具体实现
    public void algorithm() {
    //算法A
    }
}
class Context {
    private AbstractStrategy strategy; //维持一个对抽象策略类的引用
    public void setStrategy(AbstractStrategy strategy) {
        this.strategy= strategy;
    }
    //调用策略类中的算法
    public void algorithm() {
        strategy.algorithm();
    }
}
//client代码
Context context = new Context();
AbstractStrategy strategy;
strategy = new ConcreteStrategyA(); //可在运行时指定类型
context.setStrategy(strategy);
context.algorithm();

posted @ 2019-05-28 19:15  coco.zhang  阅读(293)  评论(0编辑  收藏  举报