设计模式(三)~结构型模式(1)

代理模式

定义

代理模式:由于某些原因访问对象不适合或者不能直接引用目标对象,这时候需要给目标对象提供一个代理对象以控制对该对象的访问,代理对象作为访问对象和目标对象之间的中介。

分类
  • 静态代理

    代理类与真实主题(被代理类)一一对应,且真实主题必须事先存在。

  • 动态代理

    被代理对象与代理类之前没有耦合关系且无需事先存在。

特点

优点

  • 代理模式在访问者与目标者之间起到中介作用和保护目标对象的作用。
  • 代理对象可以扩展目标对象的功能。
  • 代理模式将访问对象与目标对象分离,在一定程度上降低了系统的耦合度。

缺点

  • 代理模式在访问对象与目标对象之间建立一个代理对象,会造成请求处理速度变慢。
  • 增加系统复杂度。
结构

代理模式结构比较简单,主要是通过定义一个抽象主题的代理来包含真实主题,从而实现对真实主题的访问。

代理模式主要角色如下:

  • 抽象主题(Subject)类:通过接口或抽象类声明真实主题的功能方法(真实主题和代理类都要实现此抽象主题)
  • 真实主题(Real Suject)类:实现了抽象主题中的具体方法,是代理对象所代表的真实对象,是最终要引用的对象。
  • 代理(Proxy)类:实现了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制和扩展真实主题的功能。
    在这里插入图片描述
实现

案例:上海房产是一家经营许多房源的公司,提供了许多房源它是真实的主题,提供房源展示方法,而上海无爱无家上海房产公司的代理,通过调用上海房产的显示方法,显示代理的产品,当然可以增加一些额外的处理,如:清扫或者加价

/**
 *1、抽象主题,定义了具体主题(被代理类)的功能(具体主题和代理类都要实现)
 */
public interface IHouse {
    //房源展示方法
    void show();
}
/**
 *2、具体主题-上海房产公司,实现抽象主题定义的方法-房源展示
 */
public class SHHouse implements IHouse {
    @Override
    public void show() {
        System.out.println("我是真实主题(被代理类)-上海房产公司,只提供顾客看房子");
    }
}
/**
 *3、代理类-无爱无家,实现了抽象主题定义的方法,内部含有对真实主题的引用,可以对被代理类功能进行扩展
 */
public class House5i5jProxy implements IHouse {
    //真实主题的引用
    SHHouse shHouse;
    public House5i5jProxy() {
        this.shHouse = new SHHouse();
    }

    @Override
    public void show() {
        cleanUPHouse();
        if (shHouse != null) {
            //调用真实主题的方法
            shHouse.show();
        }
        resetHouse();
    }

    //代理类扩展方法-清扫房子
    private void cleanUPHouse() {
        System.out.println("我是代理类-顾客看房子之前,先去打扫房子");
    }

    //代理类的扩展方法-恢复房子
    private void resetHouse() {
        System.out.println("我是代理类-顾客看房子之后,重新整理房子");
    }
}
//测试
public class Test {
    public static void main(String[] args) {
        //通过代理对象访问目标对象的方法
        House5i5jProxy house5i5jProxy = new House5i5jProxy();
        //代理对象内部对目标对象的方法进行了扩展
        house5i5jProxy.show();
    }
}

//输出
我是代理类-顾客看房子之前,我先去打扫房子
我是被代理类-上海房产公司,只提供顾客看房子
我是代理类-顾客看房子之后,我重新整理房子
应用场景
  • 远程代理:这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。
  • 虚拟代理:这种方式通常用于要创建的目标对象开销很大时。
  • 安全代理:这种方式通常用于控制不同种类客户对真实对象访问。
  • 智能指引:主要用于调用目标对象时代理附加一些额外的处理功能
  • 延迟加载:喂了提供系统的性能,延迟对目标对象的加载。
扩展(动态代理)

前面介绍的代理模式中,代理类中包含了对真实主题的引用,这种方式有两个缺陷

  • 真实主题与代理类必须要一一对应,增加真实主题也要增加代理。
  • 设计代理以前真实主题必须事先存在,不太灵活。采用动态代理可以解决上面问题。
/**
 * 抽象主题(定义真实主题(被代理类的功能方法))
 */
public interface ICar {
    void show();
}
/**
 *真实主题(实现抽象主题接口定义的方法)
 */
//真实主题-公共汽车
public class BusCar implements ICar {
    String name;
    public BusCar(String name) {this.name = name;}

    @Override
    public void show() {
        System.out.println("我是被代理类-" + name);
    }
}

//真实主题-出租车
public class TaxiCar implements ICar {
    String name;
    public TaxiCar(String name) {this.name = name;}

    @Override
    public void show() {
        System.out.println("我是被代理类-" + name);
    }
}
/**
 * 动态代理类,不与具体的真实主题(被代理类绑定)
 */
public class DynamicProxy implements InvocationHandler {
    //真实主题(被代理类Object类型,任何实现了接口的类都可以通过此动态代理实现代理需求)
    Object object;
    public DynamicProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        System.out.println("我是动态代理类-在被代理方法执行之前插入代码逻辑");
        Object result = method.invoke(object, args);
        System.out.println("我是动态代理类-在被代理方法执行之后插入代码逻辑");
        return result;
    }
}
//动态代理测试
public class Test {
    public static void main(String[] args) {
        //被代理对象的类加载器
        ClassLoader classLoader = ICar.class.getClassLoader();
        //被代理对象实现的接口
        Class mInterface = ICar.class;
        //InvocationHandler对象
        InvocationHandler dynamicHandler = new DynamicProxy(new BusCar("公共汽车"));
        //返回被代理对象
        ICar busProxy = (ICar) Proxy.newProxyInstance(classLoader, 
                        new Class[]{mInterface}, dynamicHandler);
        busProxy.show();

        ICar taxiProxy = (ICar) Proxy.newProxyInstance(ICar.class.getClassLoader(),
                         new Class[]{ICar.class}, new DynamicProxy(new TaxiCar("出租车")));
        taxiProxy.show();
    }
}

//输出
我是动态代理类-在被代理方法执行之前插入代码逻辑
我是被代理类-公共汽车
我是动态代理类-在被代理方法执行之后插入代码逻辑

我是动态代理类-在被代理方法执行之前插入代码逻辑
我是被代理类-出租车
我是动态代理类-在被代理方法执行之后插入代码逻辑

总结
  • 动态代理在静态代理基础上扩展,不在局限于真实主题(被代理对象)与代理类一一对应关系,

    静态代理中代理类内部包含对真实主题的引用,因此需要一一对应。

    动态代理中被代理的是Object类型,符合一定规则的类都可以被代理。

  • 动态代理解决了静态代理真实主题(被代理类)必须事先存在的缺陷

    静态代理中包含了对真实主题的引用,因此如果想要代理某个类,真实主题必须事先存在

适配器模式

定义

将一个类的接口转换成客户希望的另一个接口,适配器模式使得原本由于接口不兼容而不能一起动作的那些类可以一起工作。

比如:老虎和飞禽,现在目标是飞虎,在不增加实体需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。

分类
  • 类结构模式

    通过类之间的继承实现,耦合度较高,所以用的较少

  • 对象结构模式

    通过依赖方式实现(推荐)

类适配器模式的结构
在这里插入图片描述
对象适配器模式的结构
在这里插入图片描述

特点

优点

  • 提高类的复用性

    系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

  • 透明、简单

    客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单&更直接。

  • 更好的扩展型

    在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统功能。

  • 解耦性

    将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原来的代码。

  • 符合开闭原则

    同一个适配器可以把适配者类和它的子类都适配到目标接口;可以为不同的目标接口实现不同的适配器,而不需要修改适配者类。

缺点

  • 过多使用适配器,会让系统非常凌乱,不易整体进行把控。
结构

适配器模式包含一下角色

  • 目标(Target)接口:当前系统所期待的接口,可以是抽象类或者接口。
  • 适配者(Adaptee)类:指被访问和适配的已存在组件中的类。
  • 适配器(Adapter)类:是一个转换器,通过继承或者依赖适配者对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
实现

案例

背景:小明买了一台进口电视

冲突:但是进口电视要求电压(110V),与国内输出电压(220V)不兼容

解决方案:通过设置一个适配器将插头输出的220V电压转换成110V

  • 类结构模式实现(继承)

    适配器类**继承**已有对象,实现目标接口

/**
 *1、创建目标接口(期望得到的插头)能输出110V(将220V转换为110V)
 */
public interface ITargetPower {
    void convert110V();
}
/**
 *2、创建适配者类-输出220V电压(已经存在的类)
 */
public class PowerPort220V {

    //输出220V电压
    public void outPut220V() {
       //TODO...
    }
}
/**
 *3、创建电源适配器类,继承适配者实现目标接口
 * 目标是要实现输出110的功能convert110V()方法,
 * 但是原来插头没有,因此适配器需要补充上这个功能,
 * 实际上convert110V()只是调用了原来插头的outPut220V()方法,
 * 所以适配器只是将outPut220V()方法作了一层封装,
 * 封装成目标接口ITargetPower可以调用的convert110V()方法而已。
 */
public class PowerAdapter extends PowerPort220V implements ITargetPower {
    @Override
    public void convert110V() {
        this.outPut220V();
    }
}
  • 对象结构模式实现(依赖)

    适配器类**依赖**适配者类,实现目标接口

//目标接口1、适配者类2同上
/**
 *3、创建适配器,内部依赖适配者
 */
public class PowerAdapter2 implements ITargetPower {
    //关联适配者类
    PowerPort220V powerPort220V;
    
    //通过构造函数传入具体需要适配的适配者类对象。
    public PowerAdapter2(PowerPort220V powerPort220V) {
        this.powerPort220V = powerPort220V;
    }

    @Override
    public void convert110V() {
        //使用委托方式完成特殊功能
        powerPort220V.outPut220V();
    }
}
应用场景
  • 系统需要复用现有类,而该类的接口不符合系统的要求,可以使用适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  • 多个组件功能类似,但接口不统一且可能会经常切换,可使用适配器模式,是客户端可以以统一的接口使用它们。
扩展

双向适配器模式

桥接模式

定义

将抽象和实现分离,使它们可独立变化,它是用组合关系代替继承关系来实现,从而降低抽象和实现这两个可变维度的耦合度。

特点

桥接(Bridge)模式的特点:

优点

  • 抽象和实现相分离,扩展能力强。
  • 实现细节对客户透明。

缺点

  • 增加系统理解和设计难度(由于聚合关系建立在抽象层,要求开发者对抽象化进行设计和编程)。
结构

桥接模式包含一下角色

  • 抽象化(Abstract)角色:定义抽象类,包含对实例化对象的引用
  • 扩展抽象化(Refined)角色:是抽象化角色的子类,实现父类的抽象方法,并通过关联关系调用实现化对象中的相关业务方法。
  • 实现化(Implementor)角色:定义实现化接口,供扩展抽象化对象调用。
  • 具体实现化(Concrete Implementor)角色:实现了实现化接口,完成对实现化接口业务具体实现。
    在这里插入图片描述
实现
/**
 *1、定义实现化接口(生产不同颜色的汽车)
 */
 public interface IColorCar {
    String getColor();
}
/**
 *2、定义具体实现化类(实现实实现化接口,完成相关业务的实现)
 */
//生产黑色汽车
public class BlackColorCar implements IColorCar {
    @Override
    public String getColor() {
        return "黑色";
    }
}
//生产红色汽车
public class ReadColorCar implements IColorCar {
    @Override
    public String getColor() {
        return "红色";
    }
}
/**
 *3、定义抽象化角色,生产不同类型汽车
 */
public abstract class AbsTypeCar {
    //包含对实例化对象的引用
    IColorCar iColorCar;
    public AbsTypeCar(IColorCar iColorCar) {
        this.iColorCar = iColorCar;
    }
    abstract String getType();
    public abstract void show();
}
/**
 *4、定义扩展抽象化对象,是抽象化对象的子类(完成父类的抽象方法),通过关联关系调用实例化对象方法
 */
 //扩展抽象化类-公共汽车 
 public class BusTypeCar extends AbsTypeCar {
    public BusTypeCar(IColorCar iColorCar) {super(iColorCar);}

    @Override
    String getType() {return "公共汽车";}

    @Override
    public void show() {
        System.out.println(iColorCar.getColor() + getType());
    }
}

//扩展抽象化类-出租车
public class TaxiTypeCar extends AbsTypeCar {
    public TaxiTypeCar(IColorCar iColorCar) {super(iColorCar);}
    
    @Override
    String getType() {return "出租车";}

    @Override
    public void show() {
        System.out.println(iColorCar.getColor() + getType());
    }
}
//测试
public class Test {
    public static void main(String[] args) {
        IColorCar readCar = new ReadColorCar();
        AbsTypeCar busCar = new BusTypeCar(readCar);
        busCar.show();
        
        AbsTypeCar busCar2 = new BusTypeCar(new BlackColorCar());
        busCar2.show();

        IColorCar blackCar = new BlackColorCar();
        AbsTypeCar taxiCar = new TaxiTypeCar(blackCar);
        taxiCar.show();
    }
}

//输出
红色公共汽车
黑色公共汽车
黑色出租车
应用场景
  • 当一个类存在两个独立变化的纬度,且这两个维度都需要扩展时。
  • 当一个系统不希望使用继承,或使用多层次继承导致类个数急剧增加时。
  • 当一个系统需要在构建抽象化角色与实例化角色之间增加的灵活性时。
扩展

有时桥接模式可与适配器模式合用,当桥接模式的实例化接口与现有类的接口不一致时,可在两者中间定义适配器将两者连接起来。
在这里插入图片描述

发布了63 篇原创文章 · 获赞 41 · 访问量 35万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 像素格子 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览