前言

最近工作中备受打击,之前设计的很多程序都被老大否决,需要重构,让我好好看看设计模式。之前对这一块内容的确不怎么重视,感觉枯燥无聊又派不上用场。后来沉下心来研究了一番...

我靠,原来如此,之前写代码的时候怎么这么傻逼,很多问题其实在一开始设计的时候就能避免。之前写的都是些什么鬼。

我们踩过的坑,历代前辈们也踩过。可想而知,通他们多年的踩坑填坑经验后,所总结出来的23种设计模式是多么的宝贵,就是我们的“ONE PIECE” 啊。如果掌握了这个内容,对今后无论是工作中还是面试都非常重要。因此下定决心,一定要熟练掌握这块内容。记录学习过程,供自己复习也供大家一起学习。

为什么要学习设计模式的目的

在前言中我们已经大致说明,我们总结一下,一个良好的程序应该满足一下六点要求:

1. 可复用性:尽量能重用方法,比如说提取某种工具类。
2. 可读性:编程具备规范性,阅读起来不困难。
3. 可扩展性:当你写好的程序需要添加一个新功能的时候,不能说:不行!非要添加的话我要重构代码。
4. 稳定性:尽可能少的bug。
5. 高内聚:每个模块尽可能独立完成自己的功能,不依赖于模块外部的代码。
6. 低耦合:并且模块之间联系越复杂耦合度越低,就不会牵一发而动全身。否则模块A的bug甚至会导致模块B无法运行

这是多代前辈们总结出来的编程经验,如果我们的程序没有以上特点,就会出现很多BUG。我们要站在巨人的肩膀上。尽量多学习多总结,避免犯“古人”们常犯的错误。

设计模式的作用就出来了,设计模式的目的就是为了让我们的程序具备以上六点特性。

设计模式常用的七大原则

在学习设计模式之前,为了不让设计模式显得很模式,我们还必须了解一个东西,那就是程序设计七大原则(很多地方说的是六大原则,但还有一个合成复用原则也值得提出来)。

这些原则是指导模式的规则,我会给一些原则附上一个例子,来说明这个原则所要表达的意思,注意,原则是死的,人是活的,所以并不是要你完完全全遵守这些规则,否则为何数据库会有逆范式,只是在可能的情况下,请尽量遵守。

七大原则分别是:

1. 单一职责原则(Single Responsibility Principle)
2. 接口隔离原则(Interface Segregation Principle)
3. 依赖倒置原则(Dependence Inversion Principle)
4. 里氏替换原则(Liskov Substitution Principle)
5. 开闭原则(Open Close Principle)
6. 迪米特法则(Law Of Demeter)
7. 合成复用原则(Composite/Aggregate Reuse Principle CARP)

单一职责原则

看名字就能知道,我们设计的类要尽可能的只负责一项职责。比说说A类只负责A功能,B类只负责B功能,不要A类既负责A功能又负责B功能。

为什么要这样设计?

当A功能需要更新,那么就得去修改A类。如果此时A还负责B功能,就很有可能修出BUG后导致B功能的正常使用。或者说,想实现B功能却调用的A功能的接口,这样会导致程序运行混乱。总结以下几点:

1. 降低类的复杂度,一个类只负责一项职责。
2. 提高类的可读性,可维护性。
3. 降低变更引起的风险。
4. 通常情况下,我们应当遵守单一职责原则,只要逻辑足够简单,才可以在代码级别违反单一职责原则:也就是说类中的方法数量足够少,可以在方法级别保持单一职责原则。下述代码将会说明。

例如以下代码:

public class SingleResponsibility1 {
    public static void main(String args[]) throws IOException {
        Computer computer = new Computer();
        computer.add();
    }

}
//读取配置文件和计算
class Computer{
    public int add() throws NumberFormatException, IOException {
        File file = new File("D:/data.txt");
        BufferedReader br = new BufferedReader(new FileReader(file));
        int a = Integer.valueOf(br.readLine());
        int b = Integer.valueOf(br.readLine());
        return a+b;
    }
}

在这个Computer类中有一个add方法,负责读取配置文件数字,然后再进行相加。

这个类很明显违反了单一职责原则,一个类既负责了读取文件,又负责算数。大家考虑一下这样设计有没有什么问题?

问题诸多,提高代码可维护性,报错不好定位,功能耦合。。。

来看看更新后的代码是什么样子的:

public class SingleResponsibility2 {
    public static void main(String args[]) throws IOException {
        readFile readFile = new readFile();
        readFile.read("D:/data.txt");
        Computer2 computer = new Computer2();
        computer.add(readFile.getA(),readFile.getB());
    }

}
//计算
class Computer2 {
    public int add(int a, int b){
        return a + b;
    }
}
//读取配置文件
class readFile {
    private int a;
    private int b;

    public void read(String path) throws IOException {
        File file = new File(path);
        BufferedReader br = new BufferedReader(new FileReader(file));
        a = Integer.valueOf(br.readLine());
        b = Integer.valueOf(br.readLine());
    }

    public int getA() {
        return a;
    }

    public int getB() {
        return b;
    }
}

通过这样修改代码,我们实现了单一职责原则。

这样就万无一失了吗?

当然有时候也不见得,因为如果我们的代码足够简单,这样设计会提供编写代码的成本。并且同时还要修改客户端代码。

我们再来看看下面的代码:

public class SingleResponsibility3 {
    public static void main(String args[]) throws IOException {
        Computer3 computer = new Computer3();
        computer.read("D:/data.txt");
        computer.add(computer.getA(),computer.getB());
    }

}
//负责读取配置文件,并且负责计算
class Computer3 {
    private int a;
    private int b;

    public void read(String path) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(path));
        a = Integer.valueOf(br.readLine());
        b = Integer.valueOf(br.readLine());
    }

    public int getA() {
        return a;
    }

    public int getB() {
        return b;
    }

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

当然这个只是示例代码,在真正的开发环境也不可能这样用。我举这个例子只是想说明如果一个类的方法很少功能逻辑比较简单。如我们写的Computer类,只负责读取两个数,然后相加。

这样简单的类其实就可以在代码级别违反单一职责原则:也就是说类中的方法数量足够少,可以在方法级别保持单一职责原则。

还是那句话:人是活的,原则是死的。

一个优秀的代码if else应该尽量的少用,要不耦合会非常严重,去看看优秀源码也是如此。可以思考思考使用单一职责原则,用类来划分多分支。

总结

今天就到这里,一个一个慢慢吃透,一天进步一点。下一篇我们来看接口隔离原则。

07-19 21:31