使用对比的方式学习设计模式——桥接模式、代理模式、装饰器模式三者对比学习

目录

一、前言

二、桥接模式

三、代理模式

四、装饰器模式

五、三者对比(桥接模式、代理模式、装饰器模式)

5.1 三脚架图

5.2 表格:桥接模式、代理模式与装饰器模式对比

六、从扩展的角度来谈设计模式

6.1  桥接模式扩展

6.2 代理模式扩展

6.3 装饰器模式扩展

七、小结


一、前言

常用的设计模式里面很多设计模式非常相似,学习者在学习的时候容易云里雾里,如果采用对比的方式学习,可以更加清晰的看出相似设计模式的异同。

网上介绍设计模式的文章有很多,但是一般都是介绍一种设计模式的,每个设计模式举例子都不同,读者很难形成对比性学习。

笔者致力于使用对比、抽取出设计模式的异同来介绍设计模式。本文使用同一个例子来讲述桥接模式、代理模式、装饰器模式,这三种模式都遵循“优先使用组合而不是继承”的设计原则。(使用一个例子是帮助读者从根本上区分三种设计模式

二、桥接模式

官方定义:Decouple an abstraction from its implementation so that the two can vary independently.(将抽象和实现解耦,使得两者可以独立的变化。)

我的理解:桥接模式中抽象和实现是两个不同的维度,相互独立,通过组合的关系联系在一起。

每个人都有灵魂soul和肉体body,由灵魂来产生想法,由肉体来支配活动,任何一个活动或动作的产生需要灵魂和肉体同时参与,所以必须使用一种方式将灵魂和肉体组合到一起,一起操作,从业务逻辑意义上来看,因为灵魂是内在的,肉体是外在的,所以将灵魂的对象引用注入到肉体中(这是使用属性注入),这样一来,客户端只要操作肉体即可(因为灵魂注入到肉体中,所有通过操作肉体来同时操作两者)。

注意:灵魂和肉体是两个独立的维度,没有包含关系,也不会继承同一父类,两个独立维度是桥接模式的核心,也是后面桥接模式区别代理模式和装饰器模式的关键。

package mypackage1;

public class DesignPatternDemo {

    public static void main(String[] args) {

        Body body = new FootBall_Game("foot", new Playing_FootBall());  // 踢足球需要脚
        body.sport();
        body = new BasketBall_Game("hand", new Playing_BasketBall());   // 打篮球需要手
        body.sport();
    }

}

// 灵魂和肉体是两个不同的维度 不是父子继承关系 但是要组合在一起才能共同完成事情
interface Soul {
    public void sport();   //灵魂产生思想,要参与运动
}

// 灵魂要参与运动 但是仅仅灵魂这一个维度没有办法去实现参与运动 比如 踢足球需要脚 打篮球需要手 但是手脚是属于肉体 所以必须把灵魂注入肉体中
class Playing_FootBall implements Soul {

    @Override
    public void sport() {
        System.out.println("playing football");
    }

}

class Playing_BasketBall implements Soul {

    @Override
    public void sport() {
        System.out.println("playing basketball");
    }

}
// 同样 仅仅肉体这一个维度也没有办法完成踢足球和打篮球的运动 因为肉体只有手脚 没有思想 不知道要具体参与什么运动
abstract class Body {
    protected String organ;
    Soul soul;    //灵魂注入肉体

    public Body(Soul soul) {
        this.soul = soul;
    }

    abstract void sport();

}

class FootBall_Game extends Body {

    public FootBall_Game(String organ, Soul soul) {
        super(soul);
        this.organ = organ;

    }

    @Override
    void sport() {
        soul.sport();   //肉体参与运动实际上是灵魂和肉体一起参与运动
    }

}

class BasketBall_Game extends Body {

    public BasketBall_Game(String organ, Soul soul) {
        super(soul);
        this.organ = organ;
    }

    @Override
    void sport() {
        soul.sport();
    }

}

给出类图:

三、代理模式

官方定义:Provide a surrogate (代理) or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)

我的理解:实体类和代理类都是继承于与同一个父类,将实体类引用注入到代理类中,客户端通过操作代理类间接操作实体类。

新建一个ProxySoul(则之前的Soul就是实际灵魂),表示代理灵魂类,可以通过操作代理灵魂来操作实际灵魂Soul,代理灵魂ProxySoul和实际灵魂Soul都继承于InterfaceSoul类,所以它们是同一维度,这个代理模式区别桥接模式的重要特点。

package mypackage;

public class DesignPatternDemo {
    //其实在刚才的桥接模式中,就是实际灵魂注入到肉体中,客户端操作肉体即可
    //实际灵魂--肉体--客户端
    //现在的代理模式就是,实际灵魂注入到代理灵魂中,然后运行时确定真正注入到肉体中的是代理灵魂,客户端操作肉体即可
    //实际灵魂--代理灵魂--肉体--客户端
    //仅仅中间多了一个代理灵魂类而已
    public static void main(String[] args) {
        Body body1=new FootBall_Game("foot",new ProxySoul(new Playing_FootBall())); // 踢足球需要脚
        body1.sport();
        body1=new BasketBall_Game("hand",new ProxySoul(new Playing_BasketBall()));// 打篮球需要手
        body1.sport();
    }

}
interface InterfaceSoul{
    public void sport();   //灵魂产生思想,要参与运动
}
class   ProxySoul implements  InterfaceSoul{
    public  Soul _soul;//实际灵魂注入到代理灵魂类中

    public ProxySoul(Soul _soul){
        this._soul=_soul;
    }

    @Override
    public void sport() {   //代理灵魂没有具体的产生思想的类,踢足球Playing_FootBall还是打篮球Playing_BasketBall,它只要调用实际灵魂的这些产生思想的类就好了
        _soul.sport();
    }
}
class Soul implements  InterfaceSoul {

    @Override
    public void sport() {}
}

class Playing_FootBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing football");
    }

}

class Playing_BasketBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing basketball");
    }

}

abstract class Body {
    protected String organ;
    InterfaceSoul soul;    //灵魂注入肉体   这里是面向接口编程,运行时是代理灵魂

    public Body(InterfaceSoul soul) {
        this.soul = soul;
    }

    abstract void sport();

}

class FootBall_Game extends Body {

    public FootBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;

    }

    @Override
    void sport() {
        soul.sport();   //肉体参与运动实际上是灵魂和肉体一起参与运动
    }

}

class BasketBall_Game extends Body {

    public BasketBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;
    }

    @Override
    void sport() {
        soul.sport();
    }

}

给出类图:

四、装饰器模式

官方定义:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)
我的理解:实体类和装饰器类都是继承于与同一个父类,将实体类引用注入到装饰器类中,客户端通过操作装饰器类间接操作实体类。

装饰器模式就是给现有的类添加新的属性、新的方法,但是不能在现有类上做修改。所以只有两种方式,继承或组合:继承,新建一个类继承现有的类,然后给新类添加属性或方法;组合,新建一个类,将现有类引用注入新类,在新类中新增属性或方法。由于设计模式的原则是优先使用组合而非继承,所以我们要讲的装饰器模式是用组合方式实现的。

新建一个SoulDecorator,表示灵魂装饰器,可以通过操作灵魂装饰器类来操作实际灵魂Soul,灵魂装饰器SoulDecorator和实际灵魂Soul都继承于InterfaceSoul类,所以它们是同一维度,这个装饰器模式区别桥接模式的重要特点,而装饰方法extra()的存在是装饰器模式区别代理模糊的重要特点。

package mypackage;

public class DesignPatternDemo {
    //其实在最开始的桥接模式中,就是实际灵魂注入到肉体中,客户端操作肉体即可
    //实际灵魂--肉体--客户端
    //============================
    //后来的代理模式就是,实际灵魂注入到代理灵魂中,然后运行时确定真正注入到肉体中的是代理灵魂,客户端操作肉体即可
    //实际灵魂--代理灵魂--肉体--客户端
    //仅仅中间多了一个代理灵魂类而已
    //============================
    //现在的装饰器模式就是,实际灵魂注入到灵魂装饰类中,然后运行时确定真正注入到肉体中的是灵魂装饰类,客户端操作肉体即可
    //实际灵魂--灵魂装饰类--肉体--客户端
    //仅仅中间多了一个灵魂装饰类而已,而装饰类相对于代理的区别就是,代理是老老实实的代理,不添加额外操作,装饰器是要添加额外操作
    public static void main(String[] args) {
        Body body1=new FootBall_Game("foot",new SoulDecorator(new Playing_FootBall())); // 踢足球需要脚
        body1.sport();
        body1=new BasketBall_Game("hand",new SoulDecorator(new Playing_BasketBall()));// 打篮球需要手
        body1.sport();
    }

}
interface InterfaceSoul{
    public void sport();   //灵魂产生思想,要参与运动
}
class  SoulDecorator implements  InterfaceSoul{
    public  Soul _soul;//实际灵魂注入到灵魂装饰类中

    public SoulDecorator(Soul _soul){
        this._soul=_soul;
    }
    @Override
    public void sport() {   //灵魂装饰类没有具体的产生思想的类,踢足球Playing_FootBall还是打篮球Playing_BasketBall,它只要调用实际灵魂的这些产生思想的类就好了
        _soul.sport();
        extra();
    }
    private void extra(){    //装饰器相比于代理,就是多了这么一个extra方法,起额外装饰之用
        System.out.println("This is so interesting");
    }
}
class Soul implements  InterfaceSoul {

    @Override
    public void sport() {}
}

class Playing_FootBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing football");
    }

}

class Playing_BasketBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing basketball");
    }

}

abstract class Body {
    protected String organ;
    InterfaceSoul soul;    //灵魂注入肉体   这里是面向接口编程,运行时是灵魂装饰类对象

    public Body(InterfaceSoul soul) {
        this.soul = soul;
    }

    abstract void sport();

}

class FootBall_Game extends Body {

    public FootBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;

    }

    @Override
    void sport() {
        soul.sport();   //肉体参与运动实际上是灵魂和肉体一起参与运动
    }

}

class BasketBall_Game extends Body {

    public BasketBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;
    }

    @Override
    void sport() {
        soul.sport();
    }

}

给出类图:

五、三者对比(桥接模式、代理模式、装饰器模式)

5.1 三脚架图

三脚架图如下:

5.2 表格:桥接模式、代理模式与装饰器模式对比

  桥接模式 代理模式 装饰器模式
关键类 多个维度,各个维度父类和子类 代理类 装饰类
继承关系 各个维度内父类和子类继承关系,便于面向接口编程,各个维度之间类与类没有关系,相互独立 实体类和代理类都继承于同一父类,它们与父类是继承关系,便于客户端面向接口编程 实体类和装饰类都继承于同一父类,它们与父类是继承关系,便于客户端面向接口编程
组合关系 各个维度之间类与类使用组合注入方式联系 实体类和代理类之间是组合关系,将实体类引用注入代理类,在代理类中调用实体类方法或属性 实体类和装饰类之间是组合关系,将实体类引用注入装饰类,在装饰类中调用实体类方法或属性,还有装饰类自己新增的方法和属性
联系   装饰器模式=代理模式+装饰方法

六、从扩展的角度来谈设计模式

任何一个设计模式,只要遵循了设计原则,要扩展起来都是非常容易的,通过扩展这个设计模式,我们可以真正学习到这个设计模式的精华,真正知道使用这个设计模式的好处。

6.1  桥接模式扩展

我们现在对第二部分的桥接模式扩展,加上一个打排球的类:

package mypackage;

public class DesignPatternDemo {

    public static void main(String[] args) {

        Body body = new FootBall_Game("foot", new Playing_FootBall());
        body.sport();
        body = new BasketBall_Game("hand", new Playing_BasketBall());
        body.sport();

        body = new VolleyBall_Game("hand", new Playing_VolleyBall());
        body.sport();
    }

}

// 灵魂和肉体是两个不同的维度 不是父子继承关系 但是要组合在一起才能共同完成事情
interface Soul {
    public void sport();
}

// 灵魂要参与运动 但是仅仅灵魂这一个维度没有办法去实现参与运动 比如 踢足球需要脚 打篮球需要手 但是手脚是属于肉体 所以必须把灵魂注入肉体中
class Playing_FootBall implements Soul {

    @Override
    public void sport() {
        System.out.println("playing football");
    }

}

class Playing_BasketBall implements Soul {

    @Override
    public void sport() {
        System.out.println("playing basketball");
    }

}

class Playing_VolleyBall implements Soul {

    @Override
    public void sport() {
        System.out.println("playing volleyball");
    }

}

// 同样 仅仅肉体这一个维度也没有办法完成踢足球和打篮球的运动 因为肉体只有手脚 没有思想 不知道要具体参与什么运动
abstract class Body {
    protected String organ;
    Soul soul;

    public Body(Soul soul) {
        this.soul = soul;
    }

    abstract void sport();

}

class FootBall_Game extends Body {

    public FootBall_Game(String organ, Soul soul) {
        super(soul);
        this.organ = organ;

    }

    @Override
    void sport() {
        soul.sport();
    }

}

class BasketBall_Game extends Body {

    public BasketBall_Game(String organ, Soul soul) {
        super(soul);
        this.organ = organ;
    }

    @Override
    void sport() {
        soul.sport();
    }

}

class VolleyBall_Game extends Body {

    public VolleyBall_Game(String organ, Soul soul) {
        super(soul);
        this.organ = organ;
    }

    @Override
    void sport() {
        soul.sport();
    }

}

可以看到,只要增加一个“打排球的灵魂类”、“打排球的肉体类”即可,扩展起来非常方便,桥接模式完全遵循了“对扩展开放,对修改关闭”的设计原则。

给出类图:

6.2 代理模式扩展

我们的代理模式在6.1的基础在再次扩展,再增加一个代理肉体ProxyBody:

package mypackage;
public class DesignPatternDemo {
    //实际灵魂--代理灵魂--实际肉体--代理肉体--客户端
    public static void main(String[] args) {
        InterfaceBody body=new ProxyBody(new FootBall_Game("foot",new ProxySoul(new Playing_FootBall())));// 踢足球需要脚
        body.sport();
        System.out.println("=================================");
        body=new ProxyBody(new FootBall_Game("hand",new ProxySoul(new Playing_BasketBall())));// 打篮球需要手
        body.sport();
        System.out.println("=================================");
        body=new ProxyBody(new FootBall_Game("hand",new ProxySoul(new Playing_VolleyBall())));// 打排球需要手
        body.sport();

    }

}
interface InterfaceSoul{
    public void sport();   //灵魂产生思想,要参与运动
}
class   ProxySoul implements  InterfaceSoul{
    public  Soul _soul;//实际灵魂注入到代理灵魂类中

    public ProxySoul(Soul _soul){
        this._soul=_soul;
    }

    @Override
    public void sport() {   //代理灵魂没有具体的产生思想的类,踢足球Playing_FootBall还是打篮球Playing_BasketBall,它只要调用实际灵魂的这些产生思想的类就好了
        _soul.sport();
    }
}
class Soul implements  InterfaceSoul {

    @Override
    public void sport() {}
}

class Playing_FootBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing football");
    }

}

class Playing_BasketBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing basketball");
    }

}

class Playing_VolleyBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing volleyball");
    }

}

interface  InterfaceBody{
    void sport();
}
class ProxyBody implements InterfaceBody{
    protected  Body _body;
    public  ProxyBody(Body _body){
        this._body=_body;
    }
    @Override
    public void sport() {
        _body.sport();
    }
}
abstract class Body implements InterfaceBody{
    protected String organ;
    InterfaceSoul soul;    //灵魂注入肉体   这里是面向接口编程,运行时是代理灵魂

    public Body(InterfaceSoul soul) {
        this.soul = soul;
    }

    public abstract void sport();

}

class FootBall_Game extends Body {

    public FootBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;

    }

    @Override
    public void sport() {
        soul.sport();   //肉体参与运动实际上是灵魂和肉体一起参与运动
    }

}

class BasketBall_Game extends Body {

    public BasketBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;
    }

    @Override
   public void sport() {
        soul.sport();
    }

}
class VolleyBall_Game extends Body {

    public VolleyBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;
    }

    @Override
   public void sport() {
        soul.sport();
    }

}

可以看到,只要增加一个代理肉体类ProxyBody即可。

给出类图:

6.3 装饰器模式扩展

我们的装饰器模式在6.1的基础在再次扩展,再增加一个肉体装饰器BodyDecorator:

package mypackage;

public class DesignPatternDemo {
    public static void main(String[] args) {


        InterfaceBody body=new BodyDecorator(new FootBall_Game("foot",new SoulDecorator(new Playing_FootBall())));// 踢足球需要脚
        body.sport();
        System.out.println("=================================");
        body=new BodyDecorator(new FootBall_Game("hand",new SoulDecorator(new Playing_BasketBall())));// 打篮球需要手
        body.sport();
        System.out.println("=================================");
        body=new BodyDecorator(new FootBall_Game("hand",new SoulDecorator(new Playing_VolleyBall())));// 打排球需要手
        body.sport();

    }

}
interface InterfaceSoul{
    public void sport();   //灵魂产生思想,要参与运动
}
class  SoulDecorator implements  InterfaceSoul{
    public  Soul _soul;//实际灵魂注入到灵魂装饰类中

    public SoulDecorator(Soul _soul){
        this._soul=_soul;
    }
    @Override
    public void sport() {   //灵魂装饰类没有具体的产生思想的类,踢足球Playing_FootBall还是打篮球Playing_BasketBall,它只要调用实际灵魂的这些产生思想的类就好了
        _soul.sport();
        extra();
    }
    private void extra(){    //装饰器相比于代理,就是多了这么一个extra方法,起额外装饰之用
        System.out.println("This is so interesting");
    }
}
class Soul implements  InterfaceSoul {

    @Override
    public void sport() {}
}

class Playing_FootBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing football");
    }

}

class Playing_BasketBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing basketball");
    }

}
class Playing_VolleyBall extends Soul {

    @Override
    public void sport() {
        System.out.println("playing volleyball");
    }

}


interface  InterfaceBody{
    void sport();
}
class BodyDecorator implements InterfaceBody{
    protected  Body _body;
    public  BodyDecorator(Body _body){
        this._body=_body;
    }
    @Override
    public void sport() {
        _body.sport();
        extraBody();
    }

    public void extraBody(){
        System.out.println("This body is so health");
    }
}

abstract class Body implements InterfaceBody{
    protected String organ;
    InterfaceSoul soul;    //灵魂注入肉体   这里是面向接口编程,运行时是灵魂装饰类对象

    public Body(InterfaceSoul soul) {
        this.soul = soul;
    }

   public abstract void sport();

}

class FootBall_Game extends Body {

    public FootBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;

    }

    @Override
  public   void sport() {
        soul.sport();   //肉体参与运动实际上是灵魂和肉体一起参与运动
    }

}

class BasketBall_Game extends Body {

    public BasketBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;
    }

    @Override
  public   void sport() {
        soul.sport();
    }

}

class VolleyBall_Game extends Body {

    public VolleyBall_Game(String organ, InterfaceSoul soul) {
        super(soul);
        this.organ = organ;
    }

    @Override
    public void sport() {
        soul.sport();
    }

}

可以看到,只要增加一个肉体装饰类BodyDecorator即可。

给出类图:

七、小结

        本文使用同一个例子(Body && Soul)阐述桥接模式、代理模式、装饰器模式三者区别,相同点是三种设计模式都遵循了“优先使用组合而非继承”的设计原则,如桥接模式中soul引用注入Body类,代理模式中soul引用注入到ProxySoul类,装饰器模式中soul引用注入到SoulDecorator类;不同点在于桥接模式为多个维度,代理模式、装饰器模式继承于同一父类,而代理模式与装饰器模式区别在于是否具有额外的装饰方法。第六部分再次使用扩展的方式来看三种模式。

天天打码,天天进步!

发布了177 篇原创文章 · 获赞 31 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_36963950/article/details/104065034