设计模式-结构模式

设计模式分为:创建模式、结构模式、行为模式

结构模式:

(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-

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值