• Java 的设计模式之一装饰者模式


    刚开始接触装饰者的设计模式,感觉挺难理解的,不够后来花了一个晚上的时间,终于有头绪了

    装饰者设计模式:如果想对已经存在的对象进行装饰,那么就定义一个类,在类中对已经有的对象进行功能的增强或添加另外的行为,这个类就叫装饰者类。被修饰的类叫被装饰者类,是已经存在有的功能。在装饰者类之间又可以互相装饰

    特点:
              1.装饰类通过构造方法来接收被装饰者的对象,调用它里面的功能或行为
              2. 基于对被装饰对象的功能进行扩展,提供更强大的功能

    Java中的IO流是典型的装饰者模式

    下面来看一行简短的代码:

    扩展一个接口,定义一个抽象的方法,这个接口实际上就是一个被装饰者类

    interface Work {
        public void work();
    }

    画画类:

    class Drawing implements Work { //实现接口
        
        @Override
        public void work() {  //必须实现接口中的方法
            // TODO Auto-generated method stub
            System.out.println("画画");
        }
    }

    上色类:

    class Colour implements Work {
        
        Work w;//在内部维护一个被装饰的类
        
        public Colour(Work w) {
            this.w = w;
        }
        @Override
        public void work() {
            
            w.work();
            System.out.println("给画上色");
        }
    }

    装裱类:

    class Mounting implements Work {
        
        Work w;//在内部维护一个被装饰的类
            
        public Mounting(Work w) {
            
            this.w = w;
        }
            
        @Override
        public void work() {
            
            w.work();
            System.out.println("给画装裱");
        }
    }

    测试类:

    public class Test {
    
        public static void main(String[] args) throws FileNotFoundException {
            
            Work w = new Drawing();
            Colour c = new Colour(w);
            Mounting m = new Mounting(c);
            m.work();
        }
    }

    上面是一个简单的装饰者模式。

    装饰者模式的设计原则:
                    1.多用组合,少用继承
                   继承的子类实现父类的行为,是在编译时静态决定的,而且继承的行为是相同的,但是利用装饰者之间的相互修饰(组合)
              就可以扩展出强大的功能,可以动态的进行扩展,也可以撤销
                2.对扩展开放,对修改关闭
                  扩展是在继承的前提下实现,在子类中修改。扩展是在继承被装饰者类实现行为或功能,不需要修改装饰者的类,只是在此基础        上,装饰另外的功能或行为

    需求:定义一个人吃面,可以加生菜,也可以加辣椒,也可以都加

    EatNoodle类:

    class EatNoodle {
        
        public void eat() {
            
            System.out.print("吃面");
        }
    }

    装饰者类

    class SunEatNoodle1 {
        
        EatNoodle eatNd;
        
        public SunEatNoodle1(EatNoodle eatNd) {  //定义构造方法来接收被装饰者的对象
            
            this.eatNd = eatNd;
        }
        
        public void eat() {
            
            eatNd.eat();
            System.out.println("加点香菜,感觉好吃");
        }
    }

    装饰者之间的相互修饰

    class SunEatNoodle2 {
        
        //EatNoodle eatNd;
        SunEatNoodle1 sunEat1;
        public SunEatNoodle2( SunEatNoodle1 sunEat1) {   //定义构造方法来接收装饰者的对象
            
            //this.eatNd = eatNd;
            this.sunEat1 = sunEat1;
        }
        
        public void eat() {
            
            System.out.println();
            //eatNd.eat();
            sunEat1.eat();
            System.out.println("再来点辣椒,就更好吃了");
        }
    }

    测试类:

    public class Demo1 {
    
        public static void main(String[] args) {  
            
            EatNoodle eatNoodle = new EatNoodle();
            //eatNoodle.eat();
            
            SunEatNoodle1 sunEat1 = new SunEatNoodle1(eatNoodle);
            sunEat1.eat();
            
            SunEatNoodle2 sunEat2 = new SunEatNoodle2(sunEat1);//装饰者类之间的相互修饰
            sunEat2.eat();
        }
    
    }

    运行结果:

    装饰者与继承的关系

    在添加 不同功能的时候。我们会想到了用继承来实现。而且刚学装饰者模式的时候,觉得挺难理解的,装饰者的前世今生就是继承。那为什么不用继承呢?这样岂不是更容易理解,用起来也挺方便。下面通过一个简单的Demo做一下比较。

    需求:用继承来实现通过readLine读取代码  ,每一行加上一个行号和分号的功能

    //1.添加行号,默认继承BufferedReader
    class BufferedReaderLineNum extends BufferedReader {
        
        int count = 0;
        
        public BufferedReaderLineNum(BufferedReader in) { //Reader:默认创建一个目标文件的缓冲字符输入流的大小         
            super(in);
        }
        
        @Override//3.重写父类的readLine方法
        public String readLine() throws IOException {
            
            String countent = super.readLine();//调用父类默认的行号
            
            if (countent == null) {
                return null;
            }
            
            //System.out.println(count + " " + countent);
            count++;
            return count + " " + countent;
        }
    }
    
    //添加分号
    class BufferedReaderSemicolon extends BufferedReader {
    BufferedReader reader;
    public BufferedReaderSemicolon(BufferedReader in) {//Reader:默认创建一个目标文件的缓冲字符输入流的大小 super(in); this.reader = in; } @Override//3.重写父类的readLine方法 public String readLine() throws IOException { String countent = reader.readLine(); if (countent == null) { //判断数据已经读完 return null; } return countent + ";"; } } public class Demo1 { public static void main(String[] args) throws IOException { //FileReader用于字符输入流,FileInputStream是用字节输入流 //1.创建通道,拿到一个指定的目标文件 FileReader fileRead = new FileReader("C:\java\decorator\Test.java"); BufferedReader reader = new BufferedReader(fileRead); //2.创建行号的缓冲流 BufferedReaderLineNum readerLineNum = new BufferedReaderLineNum(reader); //.创建分号的缓冲流 BufferedReaderSemicolon readerSemicolon = new BufferedReaderSemicolon(readerLineNum); String content = null; //使用readLine(),包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null while((content = readerSemicolon.readLine()) != null) { System.out.println(content); } //4.关闭资源 readerLineNum.close(); } }

    然后再通过装饰者模式来实现上面的功能:

    //添加行号
    class BufferederReadLineNum extends BufferedReader {
        
        BufferedReader bufferedReader;    //1.内部维护的被装饰者的类
        int count = 1;
        
        public BufferederReadLineNum(BufferedReader read) {
            super(read);//如果没有这行代码,会显示编译报错,显示没有隐式性的给这个构造函数定义
            this.bufferedReader = read;
        }
    
        @Override
        public String readLine() throws IOException {
            
            String countent = bufferedReader.readLine();
            
            if(countent == null) {
                return null;
            }
            
            countent = count + " " + countent;
            count++;
            return countent;
        }
    }
    
    //2.添加分号
    class BufferedReaderSemicolon1 extends BufferedReader {
        
        BufferedReader bufferedReader;    //1.内部维护的被装饰者的类
        
        public BufferedReaderSemicolon1(BufferedReader read) { 
            super(read);
            this.bufferedReader = read;
        }
        
        @Override//3.重写父类的readLine方法
        public String readLine() throws IOException {
            
            String countent = super.readLine();  //调用父类默认的行号
            if (countent == null) {   //判断数据已经读完
                return null;
            }
            
            return countent + ";";
        }
    }
    
    
    
    public class Test2 {
    
        public static void main(String[] args) throws IOException {
            
            //1.创建通道,拿到一个指定的目标文件
            FileReader fileRead = new FileReader("C:\java\代码\src\Test.java");
            
            //2.创建缓冲区 
            BufferedReader bufferedReader = new BufferedReader(fileRead);
                    
            //3创建分号的缓冲流  
            BufferedReaderSemicolon1 readerSemicolon = new BufferedReaderSemicolon1(bufferedReader);
            
            //创建行号的缓冲流    在装饰者创建的
            BufferederReadLineNum readerLineNum = new BufferederReadLineNum(readerSemicolon);
                    
            //4.读取数据
            String content = null;
            //使用readLine(),包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
            while((content = readerLineNum.readLine()) != null) {
                System.out.println(content);
            }
            //5.关闭资源
            readerLineNum.close();
            
        }
    }

    通过上面的代码,可能不能完全看出使用装饰者模式的强大功能,你可以在多创建几个类,添加多个功能,结果就显而易见了。

    在实现多个功能的时候,使用继承的体系会过于庞大,显得很臃肿。

    使用了装饰者来进行动态性的添加一些附加功能,确保在运行时,不用改变该对象的结构就可以在外部添加附加的功能。通常也是通过继承来实现给定类的功能扩展来实现更强大的功能。提高了代码的可维护性和简洁性。

  • 相关阅读:
    c#中文字符串与byte数组互相转化
    c#的中英文混合字符串截取 public static string SubString(string inputString, int byteLength)
    c#的中英文混合字符串截取指定长度,startidx从0开始
    //字符是否为汉字
    //获得字节长度
    C# 截取中英文混合字符串分行显示宽度相同
    C#截取中英文混合字符串分行显示
    C#截取指定长度中英文字符串方法 (修改)
    截取字符串的长度(中英文)
    canvas贪吃蛇游戏
  • 原文地址:https://www.cnblogs.com/123hqb--/p/6171285.html
Copyright © 2020-2023  润新知