Java的设计模式(二)

装饰器模式

一、概念
    装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,他是作为现有的类的一个包装。
    这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
二、介绍
    意图:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更加灵活。
    主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
    如何解决:将具体功能职责划分,同时继承装饰者模式。
    关键代码:Component类充当抽象角色,不应该具体实现;修饰类引用和继承Component类,具体扩展类重写父类方法。
    应用实例:孙悟空有72变,当它变成“庙宇”后,他的根本还是一只猴子,但是它又有了庙宇的功能。
    优点:装饰类和被装饰类可以独立发展,不会相互耦合。装饰器模式继承的替代模式。装饰器模式可以动态扩展一个实现类的功能
    缺点:多层装饰比较复杂
    使用场景:扩展一个类的功能;动态增加功能,动态撤销。
三、实现
    1、抽象组件:一个接口或抽象类,里面有想要实现的抽象方法。

public interface Shape {
     void draw();
 }

    2、具体组件:实现接口,实现接口里的方法,可以多个。

public class Circle implements Shape{
     @Override
     public void draw() {
         System.out.println("Shape:circle!");
     }
 }

public class Rectangle implements Shape{
     @Override
     public void draw() {
         System.out.println("shape:rectangle!");
    }
 }

    3、抽象装饰类:实现Shape接口,(一个Shape类型的变量 可有可无),实现抽象方法。这么做是为了输出具体组建中draw方法的内容。

public class ShapeDecorator implements Shape{
     protected Shape decoratorShape;
     public ShapeDecorator(Shape decoratorShape) {
         this.decoratorShape = decoratorShape;
     }
    
     @Override
     public void draw() {
         decoratorShape.draw();
     }
}

    4、具体装饰类:继承抽象装饰类ShapeDecorator,通过构造方法调用父类构造方法,实现抽象方法。 可以多个

public class RedShapeDecorator extends ShapeDecorator{
     public RedShapeDecorator(Shape decoratorShape) {
         super(decoratorShape);
     }
     public void draw() {
         decoratorShape.draw();
         setRedBorder(decoratorShape);
     }

     private void setRedBorder(Shape decoratorShape) {
         System.out.println("Border Color:Red");
     }
}

    5、测试装饰器模式:创建对象,调用方法。 先创建具体组件对象,然后具体装饰类对象(具体组件对象),具体装饰类对象调用方法。

public class DecoratorPatternDemo {
     public static void main(String[] args) {
         Shape circle = new Circle();
         ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
         ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
         System.out.println("Circle with normal border!");
         circle.draw();
         System.out.println("\n circle with red border!");
         redCircle.draw();
         System.out.println("\n rectangle with red border!");
         redRectangle.draw();
     }
}

运行结果:

    总结:装饰器模式在不改变原先核心功能的情况下,可以实现增强,并且不会产生很多继承类,按照业务模块划分,通过不同的方法进行装饰。

代理模式

    一个真实的对象RealSubject提供一个代理对象Proxy。通过Proxy可以调用realSubject的部分功能,并添加一些额外的业务处理,同时可以屏蔽RealSubject中未开放的接口。
    RealSubject是委托类,Proxy是代理类
    Subject是委托类和代理类的接口
    request()是委托类和代理类的共同方法

一、介绍
    一个代理类代理一个真实类的功能,替原对象进行一些操作。比如租房子的时候会去找中介,买火车票的时候找黄牛代理,但是退票改票需要本人操作。
    主要解决:直接访问对象时带来的问题,直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
    应用实例:Windows历史快捷方式;一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制;spring aop。
    优点:职责清晰;高扩展性;智能化
    缺点:由于在客户端和真实主体之间增加了代理对象,因此有些类的代理模式可能会造成请求的处理速度变慢;;实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
    使用场景:1、远程代理。2、虚拟代理。3、Copy-on-Write代理。4、保护(Protect or Access)代理。5、Cache代理。6、防火墙处理。7、同步化代理。8、智能引用代理。
    和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
    和装饰器模式的区别:装饰器模式为了增加功能,而代理模式是为了加以控制。

二、实现
    代理实现分为静态代理和动态代理。
    1、静态代理
        静态代理模式其实很常见,比如买火车票,黄牛相当于是火车站的代理,我们可以通过黄牛买票,但是还能去火车站进行退票和改签。

    接口

public interface Subject {
     void request();
}

    实现接口的类

//委托类
public class RealSubject implements Subject{
     @Override
     public void request() {
         System.out.println("RealSubject");
     }
 }

//代理类
public class Proxy implements Subject{
     private Subject subject;
     public Proxy(Subject subject) {
         this.subject = subject;
     }

     @Override
     public void request() {
         System.out.println("begin");
         subject.request();
         System.out.println("end");
     }
}

测试:

public class ProxyTest {
     public static void main(String[] args) {
         RealSubject subject = new RealSubject();
         Proxy proxy = new Proxy(subject);
         proxy.request();
     }
}

// 结果
begin
RealSubject
end

    2、动态代理
        动态代理中,代理类并不是在java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理分为jdk动态代理和cglib动态代理。
        定义业务逻辑

public interface Service {
     public abstract void add();
}

public class UserServiceImpl implements Service{
     @Override
     public void add() {
         System.out.println("this is add service");
     }
 }

    利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口定义代理类的实现。

public class MyInvocationHandler implements InvocationHandler{
     private Object target;
     public MyInvocationHandler(Object target) {
         this.target = target;
     }

     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         System.out.println("------begin------");
         Object result = method.invoke(target,args);
         System.out.println("------end------");
         return result;
     }

     public Object getProxy() {
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
         Class<?>[] interfaces = target.getClass().getInterfaces(); 
         return Proxy.newProxyInstance(loader, interfaces, this);
     }
}

使用动态代理

public class ProxyTest {
     public static void main(String[] args) {
         Service service = new UserServiceImpl();
         MyInvocationHandler handler = new MyInvocationHandler(service);
         Service serviceProxy = (Service) handler.getProxy();
         serviceProxy.add();
     }
}

执行结果:
------begin------
this is add service
------end------

三、代理对象的生成过程由Proxy类的newProxyInstance方法实现:
    1、ProxyGenerator.generateProxyClass方法负责生成代理类的字节码,生成逻辑比较复杂。
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
    2、native方法Proxy.defineClass0()负责字节码加载的实现,并返回对应的Class对象。
            Class clazz = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
    3、利用clazz.newInstance反射机制生成代理类的对象;

观察者模式


        MySubject类就是我们的主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。AbstractSubject类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象。

一、概念
    当对象间存在一对多的关系时,则使用观察者模式。比如,当一个对象被修改时,则会自动通知依赖他的对象。观察者模式属于行为型模式。
    类和类之间的关系,不涉及到继承。

二、介绍
    1、主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
    2、何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
    3、关键代码:在抽象类里有一个ArrayList存放观察者们。
    4、应用实例:拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
    优点:观察者和被观察者是抽象耦合的;建立一套触发机制
    缺点:如果一个被观察者对象有很多直接或间接观察者对象的话,将所有的观察者都通知到会花费很多时间。
    如果在观察者和观察目标之间有循环依赖的话,观察目标会触发他们之间进行循环调用,可能导致系统崩溃。
    观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
    5、使用场景:
        一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
        一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
        一个对象必须通知其他对象,而并不知道这些对象是谁。
        需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
    6、注意事项
        java中已经有了对观察者模式的支持类。
        避免循环引用
        如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式

三、实现
    1、创建Observer类

public abstract class Observer {
     protected Subject subject;
     public abstract void update();
}

    2、创建Subject类

public class Subject {
     private List<Observer> observers = new ArrayList<Observer>();
     private int state;
     public int getState() {
         return state;
     }

     public void setState(int state) {
         this.state = state;
         notifyAllObservers();
     }

     public void attach(Observer observer) {
         observers.add(observer);
     }

     public void notifyAllObservers() {
         for(Observer observer : observers) {
             observer.update();
         }
     }
}

    3、创建实体观察者类

public class BinaryObserver extends Observer{
     public BinaryObserver(Subject subject) {
         this.subject = subject;
         this.subject.attach(this);
     }

     @Override
     public void update() {
         System.out.println("Binary String:" + Integer.toBinaryString(subject.getState()));
     }
 }

public class OctalObserver extends Observer{
     public OctalObserver(Subject subject){
         this.subject = subject; this.subject.attach(this);
     }
     @Override
     public void update() {
         System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) );
     }
 }

public class HexaObserver extends Observer{
     public HexaObserver(Subject subject){
         this.subject = subject;
         this.subject.attach(this);
     }

     @Override
     public void update() {
         System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() );
     }
 }

4、使用Subject和实体观察者对象

public class ObserverPatternDemo {
     public static void main(String[] args) {
         Subject subject = new Subject();
         new HexaObserver(subject);
         new OctalObserver(subject);
         new BinaryObserver(subject);
         System.out.println("First state change: 15");
         subject.setState(15);
         System.out.println("second state change: 10");
         subject.setState(10);
     }
}

运行结果:

    观察者模式又被称作发布/订阅模式,定义了对象间一对多依赖,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新
    应用场景如下:
        对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
        对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。

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

推荐阅读更多精彩内容