Java设计模式之结构型模式

来源:互联网 发布:如何上传网站源码 编辑:程序博客网 时间:2024/05/22 10:19

在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。

一、适配器模式(Adapter)

适配器模式是指“将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本接口不兼容而不能一起工作的那些类可以一起工作,其别名是包装器(Wrapper)”。适配器可以分为类的适配器模式、对象的适配器模式、接口的适配器模式。

1.1 类的适配器模式

类适配器使用多重继承对一个接口与另一个接口进行匹配。
这里写图片描述
有一个Adaptee类,拥有一个方法,待适配,目标接口是Target,通过Adapter类,将Adaptee的功能扩展到Target里,看代码:

public class Adaptee {      public void request() {          System.out.println("this is original method!");      }  } 
public interface Target {      /* 与原类中的方法相同 */      public void request();      /* 新类的方法 */      public void specificRequest();  }  
public class Adapter extends Adaptee implements Target {      @Override      public void specificRequest() {          System.out.println("this is the Adaptee method!");      }  }  

Adapter类继承Adaptee类,实现Target接口,下面是测试类:

public class AdapterTest {      public static void main(String[] args) {          Target target = new Adapter();          target.request();          target.specificRequest();      }  }  

1.2 对象的适配器模式

对象匹配依赖于对象组合。
这里写图片描述
例子只需要修改Adapter类的源码即可

public class Adapter implements Target {      private Adaptee adaptee;      public Wrapper(Adaptee adaptee){          super();          this.adaptee = adaptee;      }      @Override      public void specificRequest() {          System.out.println("this is the Adaptee method!");      }      @Override      public void request() {          adaptee.request();      }  }  

1.3 接口的适配器模式

有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。看一下类图:
这里写图片描述

public interface Target {      public void method1();      public void method2();  }  
public abstract class Adapter implements Target {      public void method1(){}      public void method2(){}  }  
public class Adaptee1 extends Adapter {      public void method1(){          System.out.println("the Target interface's first Sub1!");      }  }  
public class Adaptee1 extends Adapter {      public void method2(){          System.out.println("the Target interface's second Sub2!");      }  }  

三种适配器模式的应用场景

  • 类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
  • 对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
  • 接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

二、装饰模式(Decorator)

装饰模式别名也叫包装器,GOF中定义为“动态的给对象添加一些额外的职责”。要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下:
这里写图片描述

public interface Sourceable {      public void method();  }  
public class Source implements Sourceable {      @Override      public void method() {          System.out.println("the original method!");      }  }  
public class Decorator implements Sourceable {      private Sourceable source;      public Decorator(Sourceable source){          super();          this.source = source;      }      @Override      public void method() {          System.out.println("before decorator!");          source.method();          System.out.println("after decorator!");      }  }  

测试类:

public class DecoratorTest {      public static void main(String[] args) {          Sourceable source = new Source();          Sourceable obj = new Decorator(source);          obj.method();      }  }  

装饰器模式的应用场景

  1. 需要扩展一个类的功能。
  2. 动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

三、代理模式(Proxy)

代理模式是指“为其他对象提供一种代理以控制这个对象的访问”。
这里写图片描述
抽象主题角色(Subject):声明了代理主题和真是主题的公共接口,是任何需要真实主题的地方都使用代理主题。
代理主题角色(ProxySubject):含有真实主题的引用,从而可以在任何时候操作真实主题的对象。
真实主体角色(RealSubject):定义了代理角色所代表的具体对象。

//抽象主题public interface Subject {    //操作    public void operation();}//目标对象角色public class RealObject implements Subject {    @Override    public void operation() {        //一些操作        System.out.println("一些操作");    }}//代理对象角色public class ProxyObject implements Subject {    RealObject realObject = new RealObject();    @Override    public void operation() {        //调用目标对象之前可以做相关操作        System.out.println("before");                realObject.operation();                //调用目标对象之后可以做相关操作        System.out.println("after");    }}

上面讲述了适配器模式,代理模式和装饰模式,感觉好像有点类似,下面我们来看看它们之间有什么区别:

代理模式与适配器模式

模式名 代理模式 适配器模式 是否替一个对象提供间接性质的访问 是 是 接口 实现和目标对象相同的接口 主要用来处理接口间不匹配的问题,它往往替所适配的对象提供一个不同的接口


代理模式与装饰模式

模式名 代理模式 装饰模式 是否需要为转调其他对象的前后执行一定的功能 是 是 目的 主要目的是控制对对象的访问 动态的为某个类型添加新的职责,也就是说为了动态的添加功能

四、门面模式(Facade)

门面模式又称外观模式,是指“外部与一个子系统的通信必须通过一个统一的门面对象进行”。简单来说,该模式就是把一些复杂的流程封装成一个接口供给外部用户更简单的使用。
这里写图片描述
门面角色:外观模式的核心。它被客户角色调用,它熟悉子系统的功能。内部根据客户角色的需求预定了几种功能的组合。
子系统角色:可以同时又一个。它对客户角色和Facade时未知的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。

下面,我们就通过一个简单的例子来实现该模式。
每个Computer都有CPU、Memory、Disk。在Computer开启和关闭的时候,相应的部件也会开启和关闭,所以,使用了该外观模式后,会使用户和部件之间解耦。如:
这里写图片描述

//cpu子系统类public class CPU {    public void start() {        System.out.println("cpu is start...");    }    public void shutDown() {        System.out.println("CPU is shutDown...");    }}
public class Memory {  //Memory子系统类    public void startup(){          System.out.println("memory startup!");      }      public void shutdown(){          System.out.println("memory shutdown!");      }  }  
public class Disk {  //Disk子系统    public void startup(){          System.out.println("disk startup!");      }      public void shutdown(){          System.out.println("disk shutdown!");      }  }  
public class Computer { //门面类Computer     private CPU cpu;      private Memory memory;      private Disk disk;      public Computer(){          cpu = new CPU();          memory = new Memory();          disk = new Disk();      }      public void startup(){          System.out.println("start the computer!");          cpu.startup();          memory.startup();          disk.startup();          System.out.println("start computer finished!");      }      public void shutdown(){          System.out.println("begin to close the computer!");          cpu.shutdown();          memory.shutdown();          disk.shutdown();          System.out.println("computer closed!");      }  }  
public class User {      public static void main(String[] args) {          Computer computer = new Computer();          computer.startup();          computer.shutdown();      }  }  

如果我们没有Computer类,那么,CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用。

使用场景

  1. 为复杂的模块或子系统提供外界访问的模块;
  2. 子系统相互独立;
  3. 在层析结构中,可以使用外观模式定义系统的每一层的入口。

五、桥接模式(Bridge)

桥接模式是指“将抽象部分与它的实现部分分离,是它们都可以独立的变化。”
这里写图片描述
Abstraction:定义抽象类的接口,维护一个指向Implementor接口的指针,将用户的请求转发给它的Implementor。RefinedAbstraction寇冲由Abstraction定义的接口。
Implementor:定义实现类的接口,该接口不一定要和Abstraction的接口定义的一样,事实上两个接口可以完全不同。
ConcretelImplementor:实现Implementor接口并定义其具体实现。

我们这里使用电视机遥控器做示例,多个电视机厂家想要遥控器厂家生产遥控器,并且遥控器的样式可能会变化。
这里写图片描述
电视机厂家遥控接口

public interface Control {    public void On();    public void Off();    public void setChannel(int ch);    public void setVolume(int vol);}

电视机厂家遥控接口实现

public class SharpControl implements Control {    @Override    public void On() {        System.out.println("***Open Sharp TV***");    }    @Override    public void Off() {        System.out.println("***Off Sharp TV***");    }    @Override    public void setChannel(int ch) {        System.out.println("***The Sharp TV Channel is setted "+ch+"***");    }    @Override    public void setVolume(int vol) {        System.out.println("***The Sharp TV Volume is setted "+vol+"***");    }}
public class SonyControl implements Control {    @Override    public void On() {        System.out.println("*Open Sony TV*");    }    @Override    public void Off() {        System.out.println("*Off Sony TV*");    }    @Override    public void setChannel(int ch) {        System.out.println("*The Sony TV Channel is setted "+ch+"*");    }    @Override    public void setVolume(int vol) {        System.out.println("*The Sony TV Volume is setted "+vol+"*");    }}

遥控器厂家抽象类接口

public abstract class TvControlabs {    Control mControl=null;    public TvControlabs(Control mControl) {        this.mControl=mControl;    }    public abstract void Onoff();    public abstract void nextChannel();    public abstract void preChannel();}

遥控器为厂家生产各种类型的遥控器

public class TvControl extends TvControlabs {    private  int ch = 0;    private  boolean ison = false;    public TvControl(Control mControl) {        super(mControl);    }    @Override    public void Onoff() {        if(ison) {            ison=false;            mControl.Off();        } else {            ison=true;            mControl.On();        }    }    @Override    public void nextChannel() {        ch++;        mControl.setChannel(ch);    }    @Override    public void preChannel() {        ch--;        if(ch<0) {            ch=200;        }        mControl.setChannel(ch);    }}
public class newTvControl extends TvControlabs {    private  int ch=0;    private  boolean ison=false;    private int prech=0;    public newTvControl(Control mControl) {        super(mControl);    }    @Override    public void Onoff() {         if(ison) {            ison=false;            mControl.Off();        } else {            ison=true;            mControl.On();        }    }    @Override    public void nextChannel() {        prech=ch;        ch++;        mControl.setChannel(ch);    }    @Override    public void preChannel() {        prech=ch;        ch--;        if(ch<0) {            ch=200;        }        mControl.setChannel(ch);    }    public void  setChannel(int nch) {        prech=ch;        ch=nch;        mControl.setChannel(ch);        }    public void   Back() {        mControl.setChannel(prech);    }}

这里各个电视机厂家的电视机都实现了Control接口,而遥控器厂家只需要设计出遥控器类型并调用各个电视机厂家的具体实现就可以了。
使用场景

  1. 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
  2. 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
  3. 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了。

六、组合模式(Composite)

组合模式又称合成模式,是用于把一组相似的对象当作一个单一的对象。将对象组合成树形结构以表示“部分-整体”的层次机构。
这里写图片描述
Component 是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component
子部件。
Leaf 在组合中表示叶子结点对象,叶子结点没有子结点。
Composite 定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除。

public abstract class Component {            String name;     public Component(String s){         this.name=s;     }     public abstract void add(Component c);     public abstract void remove(Component c);     public abstract void foreach(); }

组合类

public class Composite extends Component {    private List<Component>child=new ArrayList<Component>();    public Composite(String s) {        super(s);    }    @Override    public void add(Component c) {        child.add(c);    }    @Override    public void foreach() {        System.out.println("节点名:\t"+name);        for (Component c : child) {            c.foreach();        }    }    @Override    public void remove(Component c) {        child.remove(c);    } }

叶子节点

public class Leaf extends Component {    public Leaf(String s) {        super(s);    }    @Override    public void add(Component c) {    }    @Override    public void foreach() {        System.out.println("tself name-->"+this.name);    }    @Override    public void remove(Component c) {    }  }

测试类

public class TestComponent {    public static void main(String[] args) {             Component component = new Composite("根节点");        Component child = new Composite("一级子节点child");        Component child_1 = new Leaf("一级子节点child之子节点一");        Component child_2 = new Leaf("一级子节点child之子节点二");        child.add(child_1);        child.add(child_2);        Component child2=new Composite("一级子节点child2");        component.add(child);        component.add(child2);        component.foreach();    }}

使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。

七、享元模式(Flyweight)

享元模式是指“运用共享技术有效的支持大量细粒度的对象”。主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
这里写图片描述
FlyWeight 享元接口或者(抽象享元类),定义共享接口
ConcreteFlyWeight 具体享元类,该类实例将实现共享
UnSharedConcreteFlyWeight 非共享享元实现类
FlyWeightFactory 享元工厂类,控制实例的创建和共享

内部状态 vs. 外部状态
内部状态是存储在享元对象内部,一般在构造时确定或通过setter设置,并且不会随环境改变而改变的状态,因此内部状态可以共享。
外部状态是随环境改变而改变、不可以共享的状态。外部状态在需要使用时通过客户端传入享元对象。外部状态必须由客户端保存。

public interface FlyWeight {    void action(String externalState);}
public class ConcreteFlyWeight implements FlyWeight {    private String name;    public ConcreteFlyWeight(String name) {        this.name = name;    }    @Override    public void action(String externalState) {        System.out.pringln("name = {}, outerState = {}", this.name, externalState);    }}

享元模式中,最关键的享元工厂。它将维护已创建的享元实例,并通过实例标记(一般用内部状态)去索引对应的实例。当目标对象未创建时,享元工厂负责创建实例并将其加入标记-对象映射。当目标对象已创建时,享元工厂直接返回已有实例,实现对象的复用。

public class FlyWeightFactory {  private static ConcurrentHashMap<String, FlyWeight> allFlyWeight = new ConcurrentHashMap<String, FlyWeight>();  public static FlyWeight getFlyWeight(String name) {    if (allFlyWeight.get(name) == null) {      synchronized (allFlyWeight) {        if (allFlyWeight.get(name) == null) {          System.out.println("Instance of name = {} does not exist, creating it");          FlyWeight flyWeight = new ConcreteFlyWeight(name);          System.out.println("Instance of name = {} created");          allFlyWeight.put(name, flyWeight);        }      }    }    return allFlyWeight.get(name);  }}

使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。

原创粉丝点击