方法功能增强 - 继承、装饰者模式、动态代理

引言

  方法功能的增强是开发时常做的事情,当官方或者第三方提供的API不能满足我们的需要时,可以在原有API的基础上加上我们自定义的功能来实现需求。
  码农阶段需要掌握的功能增强的方式有三种:继承、装饰者模式、动态代理。

准备工作

  小码农以最近看的某码农培训班的视频结合自己的理解给出个例子来理解。现在有一个很火的概念:无人驾驶。假设无人驾驶的标准是Oracle公司制定的,Google想使用Java语言来开发无人驾驶系统,那么首先它需要创建一个类实现Oracle公司提供的无人驾驶的接口(接口为AIDriving,类为GoogleAIDriving)。

  • AIDriving
public interface AIDriving {
    public void start();  //无人驾驶汽车启动的方法
    public void stop();   //无人驾驶汽车停止的方法
}
  • GoogleAIDriving
public class GoogleAIDriving implements AIDriving {
    public void start() {
        System.out.println("Google汽车启动了...");
    }
    public void stop() {
        System.out.println("Google汽车停止了...");
    }
}

继承

  这个时候国内某汽车制造公司(设为A公司)想使用Google提供的无人驾驶系统。但是Google提供的系统不太适合我国国情(比如美国驾驶座在右边,中国在左边),所以A公司的工程师就想在Google系统的基础上进行定制。他们选择的方式是继承GoogleAIDriving,创建一个自己的类:A1GoogleAIDriving。

public class A1GoogleAIDriving extends GoogleAIDriving{
    public void start() {
        System.out.println("在中国启动汽车");
        super.start();
    }
    public void stop() {
        System.out.println("在中国停止汽车");
        super.stop();
    }
}

装饰者模式

  但理想很美好,现实很残忍。GoogleAIDriving被定义为一个final类(不能被继承),这个是可以理解的,因为如果GoogleAIDriving不是一个final类,任何继承GoogleAIDriving的类都可以对其start()、stop()方法进行覆盖,如果覆盖时出现bug,不能启动是小事,要是在高速上不能停止那就完蛋了。所以像启动、停止这种核心功能是不允许汽车制造商随意修改的。A公司的工程师就想到了使用装饰者模式来增强功能(设类为A2GoogleAIDriving)。
  装饰者模式该怎么做呢?首先,装饰类得和被装饰类实现相同的接口,即AIDriving;第二,在装饰类中定义一个AIDriving类型的属性,即AIDriving car;第三,有一个参数为AIDriving类型的构造函数,即A2GoogleAIDriving(AIDriving car);第四,装饰类的每个方法都要调用被装饰类相应的方法;第五,使用第三步中的构造函数创建装饰类;第六,在装饰类的方法中自定义功能。

  • 创建装饰类
public class A2GoogleAIDriving implements AIDriving {
    private AIDriving car;
    public A2GoogleAIDriving(AIDriving car) {
        this.car = car;
    }
    public void start() {
        System.out.println("在中国启动汽车...");
        car.start();
    }

    public void stop() {
        System.out.println("在中国停止汽车...");
        car.stop();
    }
}
  • 调用装饰类
public class Test {
    public static void main(String[] args) {
        GoogleAIDriving car = new GoogleAIDriving();
        A2GoogleAIDriving aCar = new A2GoogleAIDriving(car);
        aCar.start();
         /* Console : 
                在中国启动汽车...
                Google汽车启动了...       */
    }
}

动态代理

  A公司工程师正沉浸在胜利到来前的喜悦中,突然他们发现AIDriving接口有1000个方法,所以在装饰类中其他不需要加入自定义动能的998个方法我们也要调用。这一看就不是一个好的解决方案,A公司的某大佬就想起来动态代理。动态代理的详细讲解请见这里
  动态代理中可以使用反射技术得到方法的信息,如果是start()或者stop()方法就加上自定义的功能,其他方法直接执行。

  • A3GoogleAIDriving
public class A3GoogleAIDriving{
    private AIDriving car;
    public A3GoogleAIDriving(AIDriving car) {
        this.car = car;
    }
    public AIDriving getIns() {
        AIDriving a3GoogleAIDriving = (AIDriving)Proxy.newProxyInstance(
                GoogleAIDriving.class.getClassLoader(), 
                GoogleAIDriving.class.getInterfaces(), 
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        if(method.getName().equals("start")
                                &&method.getParameterTypes().length == 0) {
                            System.out.println("在中国启动汽车...");
                            return method.invoke(car, args);
                        }else if (method.getName().equals("stop")
                                &&method.getParameterTypes().length == 0) {
                            System.out.println("在中国停止汽车...");
                            return method.invoke(car, args);
                        }
                        return method.invoke(car, args);
                    }
                });
        return a3GoogleAIDriving;
    }
}
  • 执行A3GoogleAIDriving
public static void main(String[] args) throws Exception {
    GoogleAIDriving car = new GoogleAIDriving();
    A3GoogleAIDriving a3GoogleAIDriving = new A3GoogleAIDriving(car);
    AIDriving ins = a3GoogleAIDriving.getIns();
    ins.start();
    ins.stop();
    /*   Console : 
            在中国启动汽车...
            Google汽车启动了...
            在中国停止汽车...
            Google汽车停止了...   */
}

三种方式总结

  分析了三种方式的功能增强,各位大腿是不是被小码农带入了一个误区:后者比前者更好?按照存在即合理的解释,肯定不是这样的。相反,三种方法中继承却是最常见的增强方式,因为它结构简单、易于理解。而后两种都是设计模式,一般在大型软件开发时才会用到。
  而且对于装饰者模式和动态代理的区别是:装饰者模式一般用于增强功能,动态代理一般用于拦截对方法的请求。

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

推荐阅读更多精彩内容