设计模式:(三)结构性模式

结构性模式.png

一、适配器模式

适配器模式.png

下面来看看适配器的三个角色:

  • Tagret目标角色:
    该角色定义把其他类转换为何种接口,也就是我们所期望接口。
  • Adaptee源角色:
    你想把谁转换成目标角色,它使已经存在的、运行良好的类或对象,经过适配器角色包装,成为一个崭新的角色。
  • Adapter适配器模式:
    适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:把源角色转换为目标角色。

通用代码实现

目标角色

public interface Target {
    //目标角色自有的方法
    public void request();
}
public class ConcreteTarget implements Target {
    public void request (){
    
    }
}

目标角色是一个已经正式运行的角色,不可能去修改角色中的方法。

源角色

public class Adaptee {
    //原有的业务逻辑
    public void doSomething(){
    
    }
}

适配器角色

public class Adapter extends Adaptee implements Target {
    public void reqest(){
        super.doSmothing();
    }
} 

二、桥接模式

桥接模式.png

桥接模式将继承模式转化成关联关系,他降低了类与类之间的耦合度,减少了系统中类的数量,也减少了代码量。

桥接是一个接口,它与一方应该是绑定的,也就是解耦的双方中的一方必然是继承这个接口的,这一方就是实现方,而另一方正是要与这一方解耦的抽象方,如果不采用桥接模式,一般我们的处理方式是直接使用继承来实现,这样双方之间处于强链接,类之间关联性极强,如要进行扩展,必然导致类结构急剧膨胀。

uml.png

三、组合模式

组合模式.png

组合模式,就是在一个对象中包含其他对象,这些被包含的对象可能是终点对象(不再包含别的对象),也有可能是非终点对象(其内部还包含其他对象,或叫组对象),我们将对象称为节点,即一个根节点包含许多子节点,这些子节点有的不再包含子节点,而有的仍然包含子节点,以此类推


组合模式类图
  • Component抽象构件角色
    定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。

  • Leaf叶子构件
    叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。

  • Composite树枝构件
    树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。

public abstract class Component {
     //个体和整体都具有的共享
    public void doSomething(){
             //编写业务逻辑
     }
}
/**
  *树枝构件
  */
public class Composite extends Component {
     //构件容器
     private ArrayList<Component> componentArrayList = new ArrayList<Component>();
     //增加一个叶子构件或树枝构件
     public void add(Component component){
             this.componentArrayList.add(component);
     }
     //删除一个叶子构件或树枝构件
     public void remove(Component component){
             this.componentArrayList.remove(component);
     }
     //获得分支下的所有叶子构件和树枝构件
     public ArrayList<Component> getChildren(){
             return this.componentArrayList;
     }
}
//树叶节点是没有子下级对象的对象,定义参加组合的原始对象行为,其通用源代码
public class Leaf extends Component {
     /*
      * 可以覆写父类方法
      * public void doSomething(){
      * 
      * }
      */
}

四、装饰器模式

装饰器模式.png

装饰模式通用类图如下所示:


2018-11-13 下午5.12.56.png

在类图中,有四个角色需要说明:

  • Component抽象构件
    Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。

  • ConcreteComponent 具体构件
    ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是它。

  • Decorator装饰角色
    一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象的方法,在它的属性里必然有一个private变量指向Component抽象构件。

  • 具体装饰角色
    ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,你要把你最核心的、最原始的、最基本的东西装饰成其他东西

//抽象构件
public abstract class Component {
     //抽象的方法
     public abstract void operate();
}

//具体构件
public class ConcreteComponent extends Component {
     //具体实现
     @Override
     public void operate() {
             System.out.println("do Something");
     }
}

//抽象装饰者
public abstract class Decorator extends Component {
     private Component component = null;        
     //通过构造函数传递被修饰者
     public Decorator(Component _component){
             this.component = _component;
     }
     //委托给被修饰者执行
     @Override
     public void operate() {
             this.component.operate();
     }
}
//具体的装饰类
public class ConcreteDecorator1 extends Decorator {
     //定义被修饰者
     public ConcreteDecorator1(Component _component){
             super(_component);
     }
     //定义自己的修饰方法
     private void method1(){
             System.out.println("method1 修饰");
     }
     //重写父类的Operation方法
     public void operate(){
             this.method1();
             super.operate();
     }
}
public class ConcreteDecorator2 extends Decorator {
     //定义被修饰者
     public ConcreteDecorator2(Component _component){
             super(_component);
     }
     //定义自己的修饰方法
     private void method1(){
             System.out.println("method1 修饰");
     }
     //重写父类的Operation方法
     public void operate(){
             this.method1();
             super.operate();
     }
}

五、外观模式

外观模式.png

也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为发生,其通用类图如下:


外观模式类图.jpeg
  • Facade门面角色
    客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑,只是一个委托类。

  • subsystem子系统角色
    可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。

// 子系统
public class ClassA {
     public void doSomethingA(){
             //业务逻辑
     }
}
public class ClassB {
     
     public void doSomethingB(){
             //业务逻辑
     }
}
public class ClassC {
     
     public void doSomethingC(){
             //业务逻辑
     }
}

//门面对象
public class Facade {
     //被委托的对象
     private ClassA a = new ClassA();
     private ClassB b = new ClassB();
     private ClassC c = new ClassC();
     //提供给外部访问的方法
     public void methodA(){
         this.a.doSomethingA();
     }
     
     public void methodB(){
         this.b.doSomethingB();
     }
     
     public void methodC(){
         this.c.doSomethingC();
     }
}

六、享元模式

享元模式.png

要求细粒度对象,那么不可避免地使得对象数量多且性质相近,那我们就将这些对象的信息分为两个部分:内部状态(intrinsic)与外部状态(extrinsic)。
● 内部状态
内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,如id、name等,它们可以作为一个对象的动态附加信息,不必直接储存在具体某个对象中,属于可以共享的部分。

● 外部状态
外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态


享元模式类图.jpeg
  • Flyweight——抽象享元角色
    它简单地说就是一个产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。

  • ConcreteFlyweight——具体享元角色
    具体的一个产品类,实现抽象角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允许的。

  • unsharedConcreteFlyweight——不可共享的享元角色
    不存在外部状态或者安全要求(如线程安全)不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。

  • FlyweightFactory——享元工厂
    职责非常简单,就是构造一个池容器,同时提供从池中获得对象的方法。

//抽象享元角色
public abstract class Flyweight {
     //内部状态
     private String intrinsic;
     //外部状态
     protected final String Extrinsic;
     //要求享元角色必须接受外部状态
     public Flyweight(String _Extrinsic){
             this.Extrinsic = _Extrinsic;
     }
     //定义业务操作
     public abstract void operate();
     //内部状态的getter/setter
     public String getIntrinsic() {
             return intrinsic;
     }
     public void setIntrinsic(String intrinsic) {
             this.intrinsic = intrinsic;
     }
}

//具体享元角色。实现自己的业务逻辑,然后接收外部状态,以便内部业务逻辑对外部状态的依赖。
//注意,我们在抽象享元中对外部状态加上了final关键字,
//防止意外产生,什么意外?获得了一个外部状态,然后无意修改了一下,池就混乱了
public class ConcreteFlyweight1 extends Flyweight{
     //接受外部状态
     public ConcreteFlyweight1(String _Extrinsic){
             super(_Extrinsic);
     }
     //根据外部状态进行逻辑处理
     public void operate(){
             //业务逻辑
     }
}
public class ConcreteFlyweight2 extends Flyweight{
     //接受外部状态
     public ConcreteFlyweight2(String _Extrinsic){
             super(_Extrinsic);
     }
     //根据外部状态进行逻辑处理
     public void operate(){
             //业务逻辑
     }
}

//享元工厂
public class FlyweightFactory {
     //定义一个池容器
     private static  HashMap<String,Flyweight> pool= new HashMap<String,Flyweight>();
     //享元工厂
     public static Flyweight getFlyweight(String Extrinsic){
             //需要返回的对象
             Flyweight flyweight = null;
             //在池中没有该对象
             if(pool.containsKey(Extrinsic)){
                     flyweight = pool.get(Extrinsic);
             }else{
                     //根据外部状态创建享元对象
                     flyweight = new ConcreteFlyweight1(Extrinsic);
                     //放置到池中
                     pool.put(Extrinsic, flyweight);
             }
             return flyweight;
     }
}

七、代理模式

代理模式.png

代理模式通用类图:


代理模式类图.jpeg
  • Subject抽象主题角色
    抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。

  • RealSubject具体主题角色
    也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。

  • Proxy代理主题角色
    也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

//抽象主题类
public interface Subject {
     //定义一个方法
     public void request();
}

//真实主题类
public class RealSubject implements Subject {
     //实现方法
     public void request() {
             //业务逻辑处理
     }
}

//代理类
public class Proxy implements Subject {
     //要代理哪个实现类
     private Subject subject = null;    
     //默认被代理者
     public Proxy(){
             this.subject = new Proxy();
     }
     //通过构造函数传递代理者
     public Proxy(Object...objects ){
     }
     //实现接口中定义的方法
     public void request() {
             this.before();
             this.subject.request();
             this.after();
     }
     //预处理
     private void before(){
             //do something
     }
     //善后处理
     private void after(){
             //do something
     }
}

JDK 自带的动态代理:
java.lang.reflect.Proxy:生成动态代理类和对象;
java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现
对真实角色的代理访问。
每次通过 Proxy 生成的代理类对象都要指定对应的处理器对象。

  1. 接口:Subject.java
public interface Subject {
    public String speak();
}
  1. 真实对象:RealSubject.java
public class RealSubject implements Subject{
  
    @Override
    public String speak() {
        System.out.println("speak");
        return "speak";
    }
}
  1. 处理器对象:MyInvocationHandler.java
public class MyInvocationHandler implements InvocationHandler {
    /**
     * 因为需要处理真实角色,所以要把真实角色传进来
     */
    Subject realSubject ;

    public MyInvocationHandler(Subject realSubject) {
        this.realSubject = realSubject;
    }

    /**
     *
     * @param proxy    代理类
     * @param method    正在调用的方法
     * @param args      方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用代理类");
        if(method.getName().equals("speak")){
             String str = (String)method.invoke(realSubject, args);
            System.out.println("调用的是说话的方法");
            return str ;
        }
    }
}
  1. 调用端:Main.java
public static void main(String[] args) {
        //真实对象
        Subject realSubject =  new RealSubject();

        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject);
        //代理对象
        Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), 
          new Class[]{Subject.class}, myInvocationHandler);

       
        proxyClass.speak();
    }

上一篇:设计模式:(二)创建型模式
下一篇:设计模式:(四)行为型模式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容