设计模式-结构模式

来源:互联网 发布:linux zip文件压缩 编辑:程序博客网 时间:2024/05/27 19:25
设计模式分为:创建模式、结构模式、行为模式

结构模式:

(1)Proxy-代理模式

定义:为一个对象提供代理,以控制对象的访问.

优点:

  1. 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。

  2. 代理对象将被代理对象透明化.

  3. 具有较高的扩展性.被代理对象的修改不会影响代理对象及其外部调用.

缺点:

  1. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。

  2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

应用场景:

  1. 远程代理:为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。

  2. 虚拟代理:通过使用过一个小的对象代理一个大对象。这样就可以减少系统的开销。

  3. 保护代理:用来控制对真实对象的访问权限。

代理的好处:

  1. 可以在间接访问对象的同时,要其前或后,添加其它的逻辑代码.

  2. 对原来逻辑进行添加其它逻辑,最终生成新的逻辑.即:对类的方法添加一些额外的逻辑,生成新的方法逻辑

// 有一个Moveable接口public interface Moveable {    public void move();}// 有一辆Tank实现了它public class Tank implements Moveable{    public void move() {        System.out.println("Tank moving ...");        try {            Thread.sleep(new Random().nextInt(5000));        } catch (InterruptedException e) {            e.printStackTrace();        }    }}// 现在想知道这辆Tank移动了多长时间,即move方法运行的时间// 方法一:可以直接在 System.out.println("Tank moving ..."); 前后加上Long start/stop = System.currentTimeMillis();直接更改源码,但不灵活// 方法二:使用继承实现TimeTank代理类,在子类中覆盖Tank的move方法,Java中没有多继承,继承了Tank就不能继承其他类了,不推荐public class TimeTank1 extends Tank{    public void move(){        before();        super.move();        after();    }    public Long before(){        return System.currentTimeMillis();    }    public Long after(){        return System.currentTimeMillis();    }}// 方法三:使用静态代理TimeTank,在Tank调用move前后加上响应的逻辑,组合 推荐,灵活public class TankTimeProxy implements Moveable{    Moveable t;    public TankTimeProxy(Moveable t){        this.t = t;    }    public Long before(){        return System.currentTimeMillis();    }    public Long after(){        return System.currentTimeMillis();    }    public void move() {        Long start = before();        t.move();        Long stop = after();        System.out.println("Tank 运行了 "+((stop-start)/1000)+"s 时间");    }}/** -----延伸:-----如果想知道Tank的运行日志,可以日志代理类LogTank,在Tank调用move前后加上响应的逻辑,组合 推荐,灵活 */public class TankLogProxy implements Moveable{    Moveable t;    public TankLogProxy(Moveable t){        this.t = t;    }    public void move() {        System.out.println("Tank start move...");        t.move();        System.out.println("Tank stop move...");    }}// 测试:先记录日志,再计算运行时间public class Client {    public static void main(String[] args) {        Tank t = new Tank();        Moveable timeTank = new TankTimeProxy(t);        Moveable logTank = new TankLogProxy(timeTank);        logTank.move();    }}

(2)动态代理

 Java 动态代理类位于java.lang.reflect包下,一般涉及到以下两个类:

  1. interface InvocationHandler 该接口仅定义了一个方法

    invoke(Object obj, Method method, Object[] args)

    其中,obj 是指代理类 , method 被代理的方法, args 为该方法的参数数组 ,这个抽象方法在代理类中动态实现

  2. Proxy 该类即为动态代理类,主要包括

    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHander h); 返回代理类的一个实例

 所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。
 在使用动态代理类时,我们必须实现InvocationHandler接口

如Tank的时间、日志代理用动态代理的实现代码:

// 方法调用前后的时间处理public class TimeHandler implements InvocationHandler{    private Object target;    public TimeHandler(Object target) {        super();        this.target = target;    }    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        Long start = before();        method.invoke(target, new Object[]{});        Long stop = after();        System.out.println("Tank 运行了 "+((stop-start)/1000)+"s 时间");        return null;    }    public Long before(){        return System.currentTimeMillis();    }    public Long after(){        return System.currentTimeMillis();    }}// 方法调用前后的日志处理public class LogHandler implements InvocationHandler{    private Object target;    public LogHandler(Object target) {        super();        this.target = target;    }    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        System.out.println("日志--方法执行前");        method.invoke(target, new Object[]{});        System.out.println("日志--方法执行后");        return null;    }}// 测试时间:public static void main(String[] args) {    Tank t = new Tank();    InvocationHandler h = new TimeHandler(t);    Moveable m = (Moveable) Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), h);    m.move();}输出结果:Tank moving ...Tank 运行了 2s 时间// 测试时间和日志:public static void main(String[] args) {        Tank t = new Tank();        InvocationHandler h = new TimeHandler(t);        Moveable m = (Moveable) Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), h);        InvocationHandler h1 = new LogHandler(m);        Moveable m1 = (Moveable) Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), h1);        m1.move();    }执行结果:日志--方法执行前Tank moving ...Tank 运行了 4s 时间日志--方法执行后

(3)Facade-外观(门面)模式

定义:为子系统的一组接口提供统一的接口,使得子系统更容易使用.将复杂的过程包含在里面,提供一个简单的应用接口即可

优点:

  1. 减少了系统的依赖.系统只依赖于被门面模式封装好的高级接口,而不依赖于子系统的内部接口.

  2. 提高灵活性.子系统内部的改变,不会对门面对象产生影响.

  3. 提高安全性.将子系统内部的实现透明化.

缺点:

  不符合开闭原则.对于门面对象,若要修改,则需要重新写.

应用场景:

  1. 当要为访问一系列复杂的子系统提供一个简单入口时。facade类很好的屏蔽了子系统中因不断演化而产生的越来越多的小类,降低访问子系统的复杂性。

  2. 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,提高子系统的独立性和可移植性。

  3. 当需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,则可以让它们仅通过facade进行通讯,从而简化子系统间的依赖关系。

public class Facade {    PutElephantIntoBridge putElephantIntoBridge = new PutElephantIntoBridge();    public void option(){        putElephantIntoBridge.openBraidgeGate();        putElephantIntoBridge.putElephantIntoBridge();        putElephantIntoBridge.closeBraidgeGate();    }    public static void main(String[] args){        Facade facade = new Facade();        facade.option();    }}class PutElephantIntoBridge{    public void openBraidgeGate(){        System.out.println("冰箱门已打开.");    }    public void putElephantIntoBridge(){        System.out.println("把大象放入冰箱.");    }    public void closeBraidgeGate(){        System.out.println("冰箱门已关闭.");    }}

(4)Adaptor-适配器模式

定义:将一个接口转变成客户所期望的接口,从而使本来因为接口不匹配,不能在一起工作的两个接口,可以在一起工作.
将一个已存在的类/接口进行复用,将其转换/具体化成客户希望的另外的一个类/接口

优点:

  1. 可以让两个完全没有关系的接口可以一起工作(当然前提是需要适配器来进行处理).

  2. 增加了接口的透明性.具体的实现是封装在适配者中,对于客户来说是透明的.

  3. 提高了接口的复用性.

  4. 增加了灵活性.修改适配器,而不会对系统产生影响.

缺点:

对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性,不能将
一个适配者类和他的子类同时适配到目标接口.

应用场景:

  1. 系统需要使用现有的类,而这些类的接口不符合系统的需要.

  2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类.

实现Adapter方式,其实”think in Java”的”类再生”一节中已经提到,有两种方式:组合(composition)和继承(inheritance).

适配器并不是通过继承来获取适配类(原)的功能的,而是通过适配类的对象来获取的,这就解决了java不能多继承所带来的不便了。
这也是java提倡的编程思想之一,即尽量使用聚合不要使用继承。

// 客户端期望的功能:求两个数的加减法public interface Operation{    public int sum(int a, int b);    public int minus(int a, int b);}// 现有的实现有public class Sum{    public int sum(int a, int b){return a+b;}}public class Minus{    public int minus(int a, int b){return b-b;}}// 用对象适配器来完成我们需要的功能public class AdaptorOperation implements Operation{    private Sum sum;    private Minus minus;    public int sum(int a, int b){ return sum.sum(a,b);}    public int minus(int a, int b){ return minus.minus(a,b);}}

(5)Composite-组合模式

定义:将对象组合成树形结构,以表示"部分与整体"的关系,使得用户在对待单个对象和组合对象时具有一致性.

优点:

  1. 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。

  2. 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。

  3. 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。

  4. 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

缺点:

  1. 在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件

  2. 使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂

应用场景:

  1. 需要使用到部分-整体关系的场景.

  2. 从整体能够独立出部分的场景.

</pre></div><h3>(6)Decorator-装饰者模式 </h3><p><strong>定义:</strong>动态地为对象添加额外职责,比继承更加灵活.</p><p><strong>优点:</strong></p><div><p>  1. 装饰者与被装饰者相互独立,不会耦合. </p><p>  2. 良好的扩展性.</p><p>  3. 是继承的代替.</p></div><p><strong>缺点:</strong></p><div><p>多层装饰者是很复杂的.</p></div><p><strong>应用场景:</strong></p><div><p>  1. 需要扩展一个类的功能,或者增加附加功能.</p><p>  2. 需要动态地给一个对象增加功能,这些功能还需要动态地撤销.</p><p>  3. 需要对一批类进行改装或者增加附加功能. </p></div><p>通过采用组合、而非继承的手法,Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。 避免了单独使用继承带来的“灵活性差”和”多子类衍生问题”</p><p>比如工厂生产杯子容量有 300ml、500ml ,但是又要求给杯子加颜色,有red红色、green绿色,可以生产红色300ml的,也可以生产绿色300ml的</p><div><pre name="code" class="java">// 原有生产杯子的,只需定义颜色的对象,通过组合扩展原有生产类的功能:public abstract class Cup {    abstract void volume();}public class Cup300ml extends Cup{        //在类里面,添加组合接口类Color    private Color color;    //在构造器中,将组合成员以参数形式传入    public Cup300ml(Color color) {        super();        this.color = color;    }    @Override    void volume() {        System.out.print("--300ml--");        color.color();    }}public class Cup500ml extends Cup{    private Color color;    public Cup500ml(Color color) {        super();        this.color = color;    }    @Override    void volume() {        System.out.print("--500ml--");        color.color();    }}public interface Color {    public void color();}public class ColorGreen implements Color{    public void color() {        System.out.println("绿色green--");    }}public class ColorRed implements Color{    public void color() {        System.out.println("红色red--");    }}// 测试:public class Client {    public static void main(String[] args){        Color red = new ColorRed();        Color green = new ColorGreen();        //生产一个300ml红色的cup        Cup cup300mlRed = new Cup300ml(red);        cup300mlRed.volume();        //生产一个300ml绿色的cup        Cup cup300mlGreen = new Cup300ml(green);        cup300mlGreen.volume();        //生产一个500ml的cup        Cup cup500ml = new Cup500ml(red);        cup500ml.volume();    }}

(7) Flyweight-享元模式

定义:使用共享对象可以有效地支持大量的细粒度的对象.就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象。

优点:

  大大减少了应用程序中创建的对象的数量,降低了内存开销.

缺点:

  1. 提高了系统的复杂度.需要分离出外部状态和内部状态.

  2. 享元对象的状态外部化,增加了读取状态的时间.

应用场景:

  1. 系统中存在大量的相似对象.

  2. 细粒度的对象可以分离出外部状态和内部状态.

  3. 需要缓冲池的场景.

  Flyweight(享元)模式中常出现Factory模式。Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个对象存储池(Flyweight Pool)
来存放内部状态的对象。

  Flyweight的关键思路,在于新建对象时:
   先到hashtable中进行获取–>判断取得对象是否为空–>若是,则新建此对象,且放回hashtable –>若存在,则共享原来的对象

Integer 类的源码中就使用了对象缓存池,具体可看源码

public interface Car {    public void showCarName();}public class BmwCar implements Car{    private String name;    public BmwCar(){        name = "Bmw";    }    public void showCarName() {        System.out.println("The BmwCar coming...");    }}public class BenzCar implements Car{    private String name;    public BenzCar(){        name = "Benz";    }    public void showCarName() {        System.out.println("The BenzCar coming...");    }}public class CarFactory {    public static Car getCar(String name){        Car car = null;        if(name.equalsIgnoreCase("Bmw")){            car = new BmwCar();        }else if(name.equalsIgnoreCase("Benz")){            car = new BenzCar();        }else{}        return car;    }}public class CarFlyWeightFactory {    public Car car;    private Hashtable<string car=""> carPool = new Hashtable<string car="">();    public Car getCar(String name){        if(name.equalsIgnoreCase("Bmw")){            car = carPool.get(name);            if(car == null){                car = new BmwCar();                carPool.put("Bmw", car);            }        }else if(name.equalsIgnoreCase("Benz")){            car = carPool.get(name);            if(car == null){                car = new BenzCar();                carPool.put("Benz", car);            }        }else{}        return car;    }    public int getNumber(){        return carPool.size();    }}// 测试:public class Client {    public static void main(String[] args) {        CarFlyWeightFactory carFlyWeightFactory = new CarFlyWeightFactory();        Car car = carFlyWeightFactory.getCar("Bmw");        car.showCarName();        Car car1 = carFlyWeightFactory.getCar("Bmw");        car1.showCarName();        if(car1 == car){            System.out.println("同一部车来的");        }else{            System.out.println("不同一部车来的");        }        System.out.println("车的数量是:"+carFlyWeightFactory.getNumber());    }}输出结果:The BmwCar coming...The BmwCar coming...同一部车来的车的数量是:1</string></string>

(8) Bridge-

0 0
原创粉丝点击