美文网首页Java技术分享
不学无数——装饰模式

不学无数——装饰模式

作者: 不学无数的程序员 | 来源:发表于2018-09-18 17:47 被阅读13次

    装饰模式

    在开始之前

    我们可以用一个简单的例子引出来装饰模式,在小的时候,相信大家都有过这样的经历:小学每年会有好几次的考试,如果有一次成绩非常差,而且考完以后学校会有个很损的招,就是打印出来成绩单,然后让家长签字。那么拿着这个成绩单,肯定是不会直接告诉家长成绩什么的,肯定是会加一些,例如,语文考了65,就会说我们班最高的才75。如果成绩单没有排名的话,那么也会添油加醋的说排名靠前,这样父母的就会被你说的所迷惑住,忽略了真实的成绩。这样说不定还会赏你点东西,然后大笔一挥一签字,你也可以松一口气终于又混过一次了。

    其实这些东西都可以用类进行表示,类图如下

    成绩报告图

    SchoolReport 类如下

    abstract class SchoolReport{
        public abstract void report();
        public abstract void sign(String name);
    }
    
    

    FourGradesSchoolReport类如下

    class FourGradesSchoolReport extends SchoolReport{
    
        @Override
        public void report() {
            System.out.println("尊敬的家长您好:");
            System.out.println("您孩子的成绩如下: ");
            System.out.println("语文65 数学70 体育80");
        }
    
        @Override
        public void sign(String name) {
            System.out.println("家长签名: "+name);
        }
    }
    
    

    Father类如下

    public static void main(String[] args) {
        SchoolReport schoolReport = new FourGradesSchoolReport();
        schoolReport.report();
        schoolReport.sign("张三");
    }
    
    

    当然这是学习好的同学的做法,直接将成绩单展示给家长就好,像考的差的就得加上上面的修饰了。类图如下:

    经过修饰的类图

    上面的两个类不变,只是增加了修饰的类SougarFourGradesSchoolReport如下:

    class SougarFourGradesSchoolReport extends FourGradesSchoolReport{
    
        private void reportHighScore(){
            System.out.println("这次考试语文最高成绩是75,数学最高是80");
        }
    
        private void reportSort(){
            System.out.println("我在班级排名是20");
        }
    
        public void report(){
              //先汇报最高的成绩
            this.reportHighScore();
            super.report();
            //然后汇报排名
            this.reportSort();
        }
    }
    
    

    就会发现输出如下

    这次考试语文最高成绩是75,数学最高是80
    尊敬的家长您好:
    您孩子的成绩如下: 
    语文65 数学70 体育80
    我在班级排名是20
    家长签名: 张三
    
    

    直接通过继承FourGradesSchoolReport此类确实能解决现有的问题,但是现实情况是非常复杂的,如果是老爸当时喝多了直接看了成绩单就直接签字了、或者是听完汇报最高成绩以后就直接乐的直接签字了,后面排名啥的都不看了、又或者是老爸想先看排名怎么办?继续扩展?那会增加多少类?这还是一个比较简单的场景,如果需要装饰的条件非常多的话,那么每个条件都进行扩展的话,那么子类的数量会激增。而且后期维护也不好。

    因此出现问题了,扩展性不好该怎么办?聪明的程序员们想到了一个办法,专门定义一批负责装饰的类,然后根据实际的情况进行组装装饰。类图如下:

    经过改进的类图

    此时的Decorator类如下

    abstract class Decorator extends SchoolReport{
        private SchoolReport schoolReport;
        
        public Decorator(SchoolReport schoolReport){
            this.schoolReport = schoolReport;
        }
        
         @Override
         public void report() {
            this.schoolReport.report();
         }
    
         @Override
         public void sign(String name) {
            this.schoolReport.sign(name);
         }
     }
    
    

    此时如果你了解代理模式的话,可能会有疑问,这和代理模式不是一样吗?带着这个疑问读下去。

    此时的两个修饰类HighSoreDecoratorSortDecorator如下

    class HighScoreDecorator extends Decorator{
    
        public HighScoreDecorator(SchoolReport schoolReport) {
            super(schoolReport);
        }
    
        private void reportHighScore(){
            System.out.println("这次考试语文最高成绩是75,数学最高是80");
        }
    
        public void report(){
            this.reportHighScore();
            super.report();
        }
    }
    
    class SortDecorator extends Decorator{
    
        public SortDecorator(SchoolReport schoolReport) {
            super(schoolReport);
        }
    
        private void reportSort(){
            System.out.println("我在班级排名是20");
        }
    
        public void report(){
            this.reportSort();
            super.report();
        }
    }
    
    

    此时在调用的时候就可以进行随意的装饰,例如老爸想先听最高分,然后直接签名那么调用如下

    public static void main(String[] args) {
        SchoolReport schoolReport;
        schoolReport = new FourGradesSchoolReport();
        schoolReport = new HighScoreDecorator(schoolReport);
        schoolReport.report();
        schoolReport.sign("张三");
    }
    
    

    打印如下

    这次考试语文最高成绩是75,数学最高是80
    尊敬的家长您好:
    您孩子的成绩如下: 
    语文65 数学70 体育80
    家长签名: 张三
    
    

    例如老爸想要先听排名,然后听最高成绩,最后签名,那么调用如下

    public static void main(String[] args) {
        //成绩单拿过来
        SchoolReport schoolReport;
        //原装的成绩单
        schoolReport = new FourGradesSchoolReport();
        //加了最高分的成绩单
        schoolReport = new HighScoreDecorator(schoolReport);
        //加了成绩排名的成绩单
        schoolReport = new SortDecorator(schoolReport);
        schoolReport.report();
        schoolReport.sign("张三");
    }
    
    

    打印如下

    我在班级排名是20
    这次考试语文最高成绩是75,数学最高是80
    尊敬的家长您好:
    您孩子的成绩如下: 
    语文65 数学70 体育80
    家长签名: 张三
    
    

    此时我们如果想增加其他的装饰模式,只需要继承了Decorator类即可,然后在使用的时候尽情组合就行。

    装饰模式定义

    装饰模式是动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式相比生成子类更加灵活

    装饰模式的通用类图如下

    装饰模式的通用类图

    在类图中有四种角色需要说明

    • Component:Component是一个接口或者是抽象类,即定义我们最核心的对象,也就是最原始的对象,如上面的成绩单SchoolReport
    • ConcreateComponent:是最原始最基本的接口或者抽象类的实现,被装饰的对象
    • Decorator:一般是一个抽象类,实现接口或者抽象方法,它里面不一定有抽象的方法,但是它的属性中必然有一个private变量指向Component抽象构件
    • ConcreteDecoratorAConcreteDecoratorB:两个具体的装饰类,在里面需要写所想装饰的东西。

    那么接下来看装饰类通用的实现

    Component

    abstract class Component{
        public abstract void operate();
    }
    
    

    具体的实现类ConcreateComponent

    
    class ConcreateComponent extends Component{
        @Override 
        public void operate() {
            System.out.println("do something");
        }
    }
    
    

    抽象的装饰类Decorator

    abstract class Decorator extends Component {
        private Component component;
    
        public Decorator(Component component) {
            this.component = component;
        }
    
        @Override
        public void operate() {
            this. component. operate();
        }
    }
    
    

    具体的装饰类ConcreteDecoratorAConcreteDecoratorB

    class ConcreteDecoratorA extends Decorator{
    
        public ConcreteDecoratorA(Component component) {
            super(component);
        }
    
        private void methodA(){
            System.out.println("MethodA装饰");
        }
        
        public void operate(){
            this.methodA();
            super.operate();
        }
    }
    
    class ConcreteDecoratorB extends Decorator{
    
        public ConcreteDecoratorB(Component component) {
            super(component);
        }
    
        private void methodB(){
            System.out.println("MethodB装饰");
        }
        
        public void operate(){
            this. methodB();
            super.operate();
        }
    }
    
    

    此处需要主要原始方法和装饰方法的执行顺序在具体的装饰类中时固定的,如果想要不同的顺序可以通过重载实现多种执行顺序。

    装饰模式的优缺点

    优点

    • 装饰类和被装饰类可以独立发展,不会相互耦合,换句话说,Component类无需知道Decorator的类,Decorator类是从外部来扩展Component类的功能。
    • 装饰模式是继承关系的一个替代方案,我们可以看到在装饰类中Decorator无论装饰了多少层,返回的对象还是Component
    • 装饰模式可以动态的扩展一个实现类的功能

    缺点

    只需要记住一点就好:复杂

    装饰模式和代理模式的区别

    相信前面看完了装饰模式会对装饰模式有个简单的理解,装饰模式以对客户透明的方式扩展对象功能,主要凸显的是修饰、增加功能

    而代理模式是给对象提供一个代理对象,并由代理对象来控制原有对象的引用,主要凸显是控制功能。

    参考文章

    相关文章

      网友评论

        本文标题:不学无数——装饰模式

        本文链接:https://www.haomeiwen.com/subject/hbqmnftx.html