详解设计模式(二)结构型

今天我们要讲的是结构型模式,在GoF的23种设计模式中,结构型占7种。

结构型模式(Structural Pattern)描述如何将类或者对象结合在一起形成更大的结构。

结构型模式描述了两种东西,类和类的实例(即对象)。所以结构型模式可以分两种,类结构型模式对象结构型模式。类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构模式中一般只存在继承关系和实现关系;而对象结构型模式关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。

根据合成复用原则,在系统中应该尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式

 

一、适配器模式 Adapter Pattern

1. 定义

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

将一个接口转化成客户希望看到的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

 

2. 模式结构

(1)角色      Target    Adapter    Adaptee    Client

(2)类图

图1 类适配器

 图2对象适配器

(1)(2)区别很明显:类适配器中,适配器和适配者是继承关系;而在对象适配器中,适配者作为被适配器创建的对象而依赖于适配器(适配器持有适配者的引用)。

 

3. 模式分析

(1)类适配器

典型的类适配器代码如下:

public class Adapter extends Adaptee implements Target
{
        public void request()
        {
                specificRequest();//让Adapter继承Adaptee,适配者当父类,就是为了可以直接使用父类(适配者)的方法
        }
}

(2)对象适配器

典型的对象适配器代码如下:

public class Adapter extends Target
{
        private Adaptee adaptee; //关联
        public Adapter(Adaptee adaptee)
        {
              this.adaptee = adaptee; //构造注入
        }
          
        public void request()
        {
              adaptee.specificRequest();
        }
}

(3)适配器模式和桥接模式的区别

4. 模式应用

(1)AdvisorAdapter

public interface AdvisorAdapter {
    boolean supportsAdvice(Advice var1);

    MethodInterceptor getInterceptor(Advisor var1);
}

SpringAOP框架中,BeforeAdvice、AfterAdvice、ThrowsAdvice三种通知类型借助适配器模式来实现,这样的好处是使得框架允许用户向框架中加入自己想要支持的任何一种通知类型。AdvisorAdapter是一个适配器接口,它定义了自己支持的Advice,并且能把一个Advisor适配成MethodInterceptor。此处涉及很多aop的知识,以后考虑专门开一个章节讲这个。

(2)InputStreamAdapter

public class InputStreamAdapter extends InputStream {

    ImageInputStream stream;

    public InputStreamAdapter(ImageInputStream stream) {
        super();

        this.stream = stream;
    }

    public int read() throws IOException {
        return stream.read();
    }

    public int read(byte b[], int off, int len) throws IOException {
        return stream.read(b, off, len);
    }
}

这是一个典型的对象适配器,在使用InputStream时,可以通过适配器InputStreamAdapter来使用ImageInputStream中的方法。

5. 扩展

(1)默认适配器模式

也叫单接口适配器模式。很简单,就是A接口有方法1、方法2、方法3,我们只想用方法1,于是用抽象类B实现A接口,B中为每个方法提供默认实现(空方法),然后用C来继承B,可以在C中重写方法1以供我们使用。

(2)双向适配器

适配器中同时包含目标类和适配器者类的引用,两边可通过适配器互相利用。

6. 优缺点

优点:将目标类和适配者类解耦,增加了类的透明性和复用性,同时系统的灵活性和扩展性都非常好,更换适配器或者增加新的适配器都非常方便,符合“开闭原则”;

缺点:适配器类在很多编程语言中不能同时适配多个适配者类,对象适配器模式的缺点是很难置换适配者类的方法。

二、桥接模式 Bridge Pattern

1. 定义

Decouple an abstraction from its implementation so that two can vary independently.

将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体模式或接口模式。

2. 模式结构

桥接模式地结构与其名称一样,存在一条连接两个继承等级结构地桥,下面给出类图:

 角色:Abstraction、RefinedAbstract、Implementor、ConcreteImplmentor

3. 模式分析

桥接模式是一个非常有用的模式,也是理解起来相对比较复杂的一个模式。在桥接模式中体现了很多面向对象设计原则的思想,包括开闭原则、合成复用原则、里氏替换原则、依赖倒置原则等。

桥接模式从接口中分离实现功能,相比使用继承减少了子类的个数。比如抽象类X有子类A、B、C,每一个子类分别有两种实现1、2,如果使用继承,那么需要A1、A2、B1、B2、C1、C2共六个子类来表示它们的功能。而如果使用桥接模式,只需让1和2共同实现一个number接口,然后让这个number和X发生聚合关系。

4. 模式实例

5. 扩展

适配器模式和桥接模式联合使用,分别在软件开发的不同阶段。在设计初期,对于存在两个独立变化维度的类,可以将其分为抽象化和实现化两个角色,使它们可以分别变化;而在初步设计完成后,发现系统与已有类无法协同工作时,可以采用适配器模式。但是在初期也可以使用适配器模式,比如涉及大量第三方应用接口的情况。

6. 优缺点

主要优点是分离抽象接口及其实现部分,是比多继承方案更好的解决方法,桥接模式还提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,实现细节对客户透明,可以对用户隐藏实现细节;主要缺点是增加系统的理解和设计难度,且识别出系统中两个独立变化的维度并不是一件容易的事情。

三、组合模式   Composite Pattern

1. 定义

组合多个对象形成树形结构以表示“整体-部分”的结构层次。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性。组合模式又可以称为“整体-部分”模式,属于对象型结构模式,它将对象组织到树结构中,可以用来描述整体与部分的关系。

2. 模式结构

角色:

Component 抽象构件:接口或者抽象类。

Leaf 叶子构件:实现抽象构件中定义的operation()。

Composite 容器构件:提供一个集合用于存储子节点。实现了抽象构件中定义的operation(),包括那些访问及管理构件的方法,在其业务方法中可以递归调用其子节点的业务方法。

类图:

 

 

3. 模式分析

public abstract class Component{
    public abstract void add(Component c);
    public abstract void remove(Component c);
    public abstract Component getChild(int i);
    public abstract void operation();
}
public class Leaf extends Component{
    public void add(Component c){//错误提示}
    public void remove(Component c){//错误提示}
    public Component getChild(int i){return null;//错误提示}
    public void operation(){
        //实现代码
    }
}
public class Composite extends Component
{
    private ArrayList list = new ArrayList();
    
    public void add(Component c){
        list.add(c);
    }
    public void remove(Component c){
        list.remove(c);
    }
    public Component getChild(int i){
       return(Component)list.get(i);
    }
    public void operation()
    {
        for(Object obj:list){
            ((Component)obj).operation();
        }
    }
}

4. 模式应用

5. 模式扩展

(1)透明组合

抽象构件Component中声明了所有用于管理的成员对象的方法,包括add()、remove()、getChild(),这样确保了所有构件类都有相同接口。缺点是不安全,一般不用。

(2)安全组合模式

抽象构件不提供add、remove这些管理成员的方法,直接在Composite中声明。这样杜绝了叶子调用这些方法。Java AWT使用了安全组合模式。

6. 优缺点

优点在于可以方便地对层次结构进行控制,客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户不必担心自己处理的是单个对象还是整个组合结构,简化了客户端代码;缺点是设计变得更加抽象,且增加新构件时可能会产生一些问题,而且很难对容器中的构件进行限制。

四、装饰模式 Decorator Pattern

1. 定义

动态的给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器的别名相同,但他们用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。

2. 模式结构

角色:Component、ConcreteComponent、Decorator、ConcreteDecorator

结构图如下:

 

 

3. 分析

面向对象设计原则告诉我们组合优于继承。因为继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。当然,组合的缺点是要创建更多的对象。

装饰模式的核心在于抽象装饰类的设计,典型代码如下:

public class Decorator extends Component{
    
    private  Component component; //关联
    public Decorator(Component component){
        this.component = component; //构造注入
    }
    
    public void operation(){
        component.operation();
    }
}
public class ConcreteDecorator extends Decorator{
    public ConcreteDecorator(Component component){
        super(component);
    }
    public void operation(){
        super.operation();
        addedBehavior();
    }
    public void addedBehavior(){
        //新增方法
    }
}

4. 应用

抽象构件类:InputStream

抽象装饰类:FilterInputStream

具体装饰类:BufferedInputStream、ByteArrayInputStream

5. 扩展

(1)透明装饰模式(实际开发不多)

客户端完全针对抽象编程,客户端不声明具体构件类型和具体装饰类型。

(2)半透明

允许客户端在声明具体装饰者类型的对象,调用者在具体装饰者中新增的方法。缺点是不能实现多重装饰。

6. 优缺点

五、外观模式 Facade Pattern

六、享元模式 Flyweight Pattern

七、代理模式 Proxy Pattern

1. 模式动机

在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为”代理“的第三者来实现间接引用。

我们在软件开发中,有时要调用一个远程方法,需要在本地设置一个代理,使得就像调用本地方法一样来使用远程的方法,这实际上也是RMI、Web Service等的实现原理。

2. 定义

给一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做Proxy,它是一种对象结构型模式。

3. 模式结构

代理类和真实操作类,都去实现抽象操作接口,然后代理类持有真实操作类的一个引用

AbstractSubject{  method A(); }

Proxy implements AbstractSubject {

        RealSubject rs = new RealSubject()

        method A(){

               rs.A();     

       }

}

RealSubject implements AbstractSubject{ method A(){} }

4. 应用 Spring Aop

5. 优缺点

代理模式的优点在于能够协调调用者和被调用者,在一定程度上降低了系统的耦合度;其缺点在于由于客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,并且实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

 

posted @ 2022-02-28 18:15  方山客  阅读(101)  评论(0编辑  收藏  举报