设计模式六大原则

设计模式六大原则

设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。

任何不可维护的代码,都是等待过时的代码。设计模式就是从长期开发中总结出来的,用以提高类的内聚性、降低类间的耦合性、提高代码的可扩展性和可维护性的方法。

职责单一原则

  • 核心
    每个类都应该有一个单一的功能,并且该功能应该由这个类完全封装起来。也就是高内聚。

  • 简单的例子

    public interface UserService {
        
        public void login(String username, String password);
        public void register(String email, String username, String password);
        public void logError(String msg);
        public void sendEmail(String email);
        
    }
    

    这段代码很显然存在很大的问题,UserService 既要负责用户的注册和登录,还要负责日志的记录和邮件的发送,并且后者的行为明显区别于前者。这就相当于一个程序员既要编代码,中午还要给公司全体员工做午饭,并且公司的卫生也由他负责。

  • 进行更改

    UserService:

    public interface UserService {
    
        public void login(String username, String password);
        public void register(String email, String username, String password);
    
    }
    

    LogService:

    public interface LogService {
    
        public void logError(String msg);
    
    }
    

    EmailService:

    public interface EmailService {
    
        public void sendEmail(String email);
    
    }
    
  • 好处
    职责单一原则给我带来最直观的感受是:类的复杂度降低了,并且,当我们想发邮件却不知道实现哪个类的时候,这种设计模式可以快速的帮我们定位到具体哪个类可以实现这个功能。

开闭原则

  • 核心
    软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的。

  • 简单的例子

    Rectangle:

    // 矩形
    public class Rectangle {
    
        public double getWidth() {
            return width;
        }
        
        public double getHeight() {
            return height;
        }
    
    }
    

    AreaCalculator:

    // 面积计算器
    public class AreaCalculator {
    
        public double area(Rectangle shape){
            return shape.getWidth() * shape.getHeight();
        }
    
    }
    

    上面代码完全可以完成矩形面积的计算,但是,这时有一个新的需求,让我们计算圆形的面积,我们可以这样更改 AreaCalculator 代码,来满足这个需求:

    Circular:

    // 圆形
    public class Circular {
    
        public double getRadius(){
            return radius;
        }
        
    }
    

    更改后的 AreaCalculator:

    public class AreaCalculator {
    
        public double area(Object shape){
            if(shape instanceof Rectangle) {
                Rectangle rectangle = (Rectangle) shape;
                return rectangle.getWidth() * rectangle.getHeight();
            } else if (shape instanceof Circular) {
                Circular circular = (Circular) shape;
                return circular.getRadius() * circular.getRadius() * Math.PI;
            } else {
                throw new RuntimeException("There is no such type.");
            }
        }
    
    }
    

    这么更改完成,完全没有问题。但是在真实的生产环境中,情况更为复杂,更改涉及的部分较多,那样就可能导致牵一发动全身。并且,以前编写的经过测试的一些功能需要重新测试,甚至导致某些功能不可用。

  • 进行改进

    Shape:

    public interface Shape {
    
        public double area();
    
    }
    

    Rectangle:

    public class Rectangle implements Shape{
    
        public double getWidth() {
            return width;
        }
    
        public double getHeight() {
            return height;
        }
    
        public double area() {
            return getWidth() * getHeight();
        }
        
    }
    

    这样,当需求变更,需要计算圆形面积的时候,我们只需创建一个圆形的类,并实现 Shape 接口即可:

    public class Circular implements Shape {
    
        public double getRadius(){
            return radius;
        }
    
        public double area() {
            return getRadius() * getRadius() * Math.PI;
        }
    }
    

    计算三角形面积、四边形面积... 的时候,我们只需让它们去实现 Shape 接口即可,无需修改源代码。

  • 好处
    一般编写完的代码,都是经过精心设计和测试过的,如果我们对其进行修改,就需要重新测试,这个测试可能涉及到所有依赖这个方法的类,如果大量修改源代码的话,那就是个可观的工程。
    所以,开闭原则可以在保证我们代码的质量的前提下,实现功能的扩展,减少开发难度。

里氏替换原则

  • 核心
    在程序里,把父类都换成它的子类,程序的行为没有变化。
  • 思考
    里氏替换原则是开闭原则的基石,正是因为里斯替换原则才可以在不修改父类源码的情况下,实现功能的扩展。
    如果子类无法实现父类的全部功能,比如鸟类有个 fly() 方法,企鹅也是鸟类的一种,但是企鹅如果继承鸟类的话,就必须得实现 fly() 方法,这时就产生了一种"畸形",这种情况应该断开继承关系。如果强行继承的话,子类并无法完全替代父类,程序就有可能因此出现故障,比如一只企鹅在天上翱翔。

依赖倒置原则

  • 核心
    针对借口编程,不针对实现编程。

  • 简单的例子

    IntelCPU:

    public class IntelCPU {
    
        public int add(int a, int b) {
            return  a + b;
        }
    
    }
    

    Mainboard:

    // 主板
    public class Mainboard {
        // 装配英特尔 CPU
        public void setCPU(IntelCPU cpu) {
            this.cpu = cpu;
        }
    
    }
    

    当某一天,CPU 需要更换的时候,我们只能装配英特尔 CPU。
    虽然这个例子比较简单,但是在实际的开发中,我们经常会被眼前的需求所蒙蔽,而不去思考拓展性,导致每次来个新需求,都要违背开闭原则。

  • 进行改进

    CPU:

    public interface CPU {
    
        public int add(int a, int b);
    
    }
    

    IntelCPU:

    public class IntelCPU implements CPU {
    
        public int add(int a, int b) {
            return  a + b;
        }
    
    }
    

    AmdCPU:

    public class AmdCPU implements CPU {
    
        public int add(int a, int b) {
            return a + b - b + b;
        }
        
    }
    

    Mainboard:

    public class Mainboard {
    
        public void setCPU(CPU cpu) {
            this.cpu = cpu;
        }
    
    }
    
  • 好处
    依赖倒置原则的好处很明显,当需求变更的时候,我们可以很灵活的进行扩展,而不用破坏开闭原则。

接口隔离原则

  • 核心
    建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。

  • 思考
    这个原则跟单一职责原则很像,都是为了精细化管理,将功能尽可能的细化。这样,当某个功能出现问题的时候,可以快速的定位问题,并且在最小范围内修复,功能的扩展也是一样的。
    但是,接口也不能无限的小,那样会产生大量的接口,造成设计过于负责。

迪米特法则

  • 核心
    降低类间的耦合性,如果两个类不必彼此通信,那么,这两个类就不要发生直接的作用。

  • 简单的例子

    Phone:

    public class Phone {
    
        public void seeMovie(Movie movie) {
            String title = movie.getTitle();
            long totalTime = movie.getTotalTime();
            // ...
        }
    
    }
    

    电影和我们的电话并没有直接的关系,这两个类的耦合度过高,也就是,电话类需要知道电影类的具体实现细节,这两个类之间没有必要进行直接的通信。

  • 进行改进

    MovieApp:

    public interface MovieApp {
    
        public void seeMovie(Movie movie);
    
    }
    

    Phone:

    public class Phone {
    
        private MovieApp movieApp;
    
        public void setMovieApp(MovieApp movieApp) {
            this.movieApp = movieApp;
        }
    
        public void seeMovie(Movie movie) {
            movieApp.seeMovie(movie);
        }
    
    }
    
  • 好处
    低耦合、低耦合、低耦合...

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

推荐阅读更多精彩内容