为什么你总是觉得设计模式很难?

简介: 关于设计模式的书不少,网上博客也有很多。那我会想,如果我要写一篇关于设计模式的文章,它给读者带来的价值在哪里呢?如果只是机械地介绍一遍所有的设计模式,那其实没有多大意义。授人以鱼不如授人以渔,今天这篇文章我想探讨更多的,是「如何去认知和学习设计模式」,以及如何才能够真正让它为你所用,成为你编程的神兵利器,披荆斩棘。

荒腔走板


上周没更新,抱歉。周末去重庆浪了一圈,都被催更了。。。

重庆于我而言,是一座熟悉又陌生的城市。这几年些许变化,但依旧很美,熙熙攘攘,人来人往,只是多了一些商业化的气息,从外地来的游客络绎不绝。


为什么旅行?有人为美食美景,有人为风土人情。有人想认识新朋友,有人想见见老朋友。有人想找过去的自己,有人想找全新的自己。所以啊,旅行的意义在于寻找自己想要的东西,图谋不同,感受不同。


这次回重庆,与朋友见见面,喝喝茶,聊起以前时候的事,感觉又回到了那个年少轻狂的时候。虽然只有短短几个小时,但也聊了很多很多,总有说不完的话题,也总有说再见的时候。

也按照惯例说一下为什么会写这篇文章。


第一个是大家对设计模式比较感兴趣,也是每个程序员(尤其是使用面向对象语言的程序员)都应该去学习掌握的思维和工具。


第二个是平时工作中确实会用到,用了设计模式,不但能让你的程序更加健壮、可维护性更好,最主要的是,看起来逼格更高了!


第三个是设计模式确实不好学,我自己刚学设计模式的时候,老是感觉有点绕,很多,记不住。或者是看似当时理解了,转眼就忘了,真到该用的时候,又一脸懵逼,不知道该用哪种设计模式。不开玩笑,我相信你应该也有过这种感觉。

关于设计模式的书不少,网上博客也有很多。那我会想,如果我要写一篇关于设计模式的文章,它给读者带来的价值在哪里呢?如果只是机械地介绍一遍所有的设计模式,那其实没有多大意义。授人以鱼不如授人以渔,今天这篇文章我想探讨更多的,是如何去认知和学习设计模式,以及如何才能够真正让它为你所用,成为你编程的神兵利器,披荆斩棘。


关于设计模式的几个问题

首先想和大家探讨几个关于设计模式的问题,这是我在打算写这篇文章时脑海中浮现出来的一些问题,自己也尝试给出一些解答,欢迎大家交流探讨。

1 设计模式解决的是什么问题?

软件是一门工程,要实现功能很简单,但是在实现功能的基础上,还能把它做得更加容易扩展,容易应对变化,那就是软件的艺术。

你会发现,大部分时间,你并不是在从头写代码,而是在“改代码”。设计得好的代码会让你改起来非常的舒服,只需要改动很小的地方,甚至不需要任何改动,就能实现新的功能。而设计得不好的代码会让你改起来像是在排地雷,一改红一大片,这就是程序太过耦合的原因。

纵观所有的软件方法论,你会发现绝大多数都是在解决耦合的问题,都想尽力把程序做得高内聚,低耦合。

设计模式解决的是使用面向对象带来的软件复杂性,尤其是面对变化的复杂性。我们希望在最开始写代码阶段,就考虑到后来有可能发生的变化,然后把自己的程序设计得更加好一点,以后改动的代价尽量更小一点。

2 什么是设计模式的六大原则?

你去看所有关于设计模式的书和系列文章,基本上都会先介绍设计模式的六大原则。

上面提到设计模式解决的其实是使用面向对象带来的软件复杂性,那具体如何解决呢?其实就是靠的这几条前人总结的原则。

理解六大原则比理解具体的设计模式更加重要,因为具体的设计模式是“术”,而六大原则是“道”。你会发现,各种各样的设计模式,都是遵循了这些六大原则中的一条或者多条。

其中,开闭原则有些特殊。它是最“虚”的,其它原则都可以看成是开闭原则的实现。

哪怕你写的程序没有用到任何已知的设计模式,也应该尽量去遵循这六大原则,这样你写出来的程序也不会太差,基本上都具有“高内聚,低耦合”的特点。

当然了,原则这个东西有时候是可以违背的,只是你要衡量违背它的代价和带来的收益,是否值得。

3 为什么通常说23种设计模式?

因为“设计模式”这个词是由四人帮(GoF)写的一本叫《Design Patterns》的书中提出来的,这本书里面总结了23种设计模式,也是最常用的设计模式

所以设计模式其实并不止23种,只是因为这23种是比较常用的设计模式,它们分别适用于不同的场景,也覆盖了绝大多数软件设计的场景。这23种设计模式只是前人的“经验总结”或者“套路”,有时候经验还是蛮重要的,使用这些经验能够少走很多弯路。

有时候多种设计模式可以互相配合起来使用,或者在现有的23种设计模式之上有一些变化,甚至是总结出一些新的“套路”,用来解决现有23种设计模式不能解决的问题,都是正常的。设计模式并不是死的,一成不变的,它应该是灵活的。

4 怎样才能掌握设计模式?

设计模式这么多,而且那么灵活,那如何才能很好地驯服设计模式这匹野马呢?

首先要理解六大设计原则,吃透他们,理解为什么有这几个原则,如果不遵循这几种原则会怎样?理解了六大设计原则,才能更好地理解具体的设计模式。

在学习一个设计模式的时候,要理解它解决了什么问题?如果不使用这种设计模式,会有什么坏处?这样在下次面对类似的问题时,就能够想起有这么一种设计模式,可以解决这个问题。

然后可以自己写一写代码,感受一下这个设计模式是怎么用的,最好是举个例子,这样比较生动形象。再看看开源代码,看看大神们是怎么用这个设计模式的。


如何学习设计模式?

设计模式是一种“套路”,那学习设计模式也是可以有套路的。我们随便选择一个设计模式,来应用这种套路。比如“模板方法模式”。

解决了什么问题?

假如一个父类有多个子类,而有一些逻辑是所有子类都共用的。比如固定的流程,或者固定的分支逻辑。

如果不使用会怎样?

如果不使用模板方法模式,每个子类都自己实现一遍,那会产生很多重复代码,这样一旦要修改这些共用的逻辑,就需要修改每个子类。

遵循了什么原则?

  • 里氏替换原则。模板方法中共有的逻辑定义在父类中,且不是abstract的,子类不应该去覆盖这个方法(可以在父类用final关键字修饰)。需要子类去实现的方法定义为abstract的,子类必须实现。
  • 依赖倒置原则,面向接口或抽象类编程。客户端只需要依赖父类就行了,不需要依赖子类。

示例代码?

我们举一个适合使用模板方法的例子,比如注册。注册的流程是比较固定的,但注册的方式可以有很多种,比如邮箱注册或者手机注册。

首先我们来定义一个注册到发送验证码的流程:

  • 输入用户名
  • 验证用户名格式是否正确(邮箱或者手机)
  • 如果格式不正确,抛出异常;如果格式正确,就注册成功,并发送验证码。

先定义抽象的父类:

public abstract class SignUpTemplate {
    public final void signUp(String username, String password) {
        if (!isValidUserName(username)) {
            throw new RuntimeException("用户名格式不正确");
        }
        save(username, password);
        sendCode(generateCode());
    }
    protected abstract boolean isValidUserName(String username);
    protected abstract void sendCode(String code);
    private String generateCode() {
        return "随机生成一个6位数的验证码";
    }
    private void save(String username, String password) {
        // 二次加密,落库等操作
    }
}

然后分别定义子类的实现:

// email注册子类
public class EmailSignUp extends SignUpTemplate {
    @Override
    protected boolean isValidUserName(String username) {
        // 验证是否符合email格式
        return false;
    }
    @Override
    protected void sendCode(String code) {
        // 发送code到email
    }
}
// 手机号注册子类
public class PhoneSignUp extends SignUpTemplate {
    @Override
    protected boolean isValidUserName(String username) {
        // 验证是否符合手机号格式
        return false;
    }
    @Override
    protected void sendCode(String code) {
        // 发送code到手机
    }
}

客户端大概长这样:

SignUpTemplate signUp = context.getSignUp();
signUp.signUp(context.getUsername, context.getPassword);

使用这个模式的开源代码

Spring的AbstractApplicationContext类的refresh方法,就是典型的模板方法模式的应用。了解过Spring源码的朋友应该看过这段代码。

我们把上面的“套路”总结成一个思维导图,大家可以依样画葫芦,对其它设计模式也试试用这个套路,看是否能够快速理解一个设计模式。

思维导图

其中,最重要的是要明确第一步,它解决了什么问题。也是我们最需要记住的。如果你怕记不住,可以用一篇文档或者一个表格记录下来,这样你在写代码时感觉隐约需要用到某种设计模式,又想不起来具体该用哪个的时候,可以随时去查阅。

至于具体的实现细节,其实不那么重要,需要用到的时候再去查也是来得及的。

有了这个套路,还觉得设计模式难吗?

目录
相关文章
|
7月前
|
设计模式 缓存 算法
C/C++设计模式之道:选择与权衡
C/C++设计模式之道:选择与权衡
158 1
|
设计模式 SQL Java
设计模式的七大原则(设计模式必修课)
设计模式(Design Pattern)是针对于软件设计中反复出现的问题所提出来的一个解决方案,是前人对代码开发经验的总结,它不是语法规定,学好设计模式可以开阔我们的编程思维,让我们编写的程序具备可复用性、可扩展性、可读性、可靠性以及安全性等;
7812 0
|
设计模式 算法 Java
设计模式宏观篇
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。
|
7月前
|
设计模式
【设计模式】1、设计模式七大原则
【设计模式】1、设计模式七大原则
38 0
|
7月前
|
设计模式 SQL 缓存
设计模式概括认知
设计模式概括认知
45 0
|
设计模式
设计模式系列教程(03) - 设计模式分类及六大原则
设计模式系列教程(03) - 设计模式分类及六大原则
44 0
|
设计模式 Java uml
设计模式宏观-系统学习五
武侠中有修炼内功和外功之分;程序界也有,而设计模式就是程序界的内功心法之一;我们在写框架或者工程的时候都要尽可能的遵循设计原则,设计模式则是在不同场景下的具体应用。
|
设计模式 算法
设计模式一句话概括
设计模式一句话概括
55 0
|
设计模式 SQL 缓存
【设计模式】【第九章】【设计模式小结】
单一职责原则:一个类只负责一个功能领域中的相应职责 开闭原则:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展
|
设计模式 NoSQL Java
被误读的设计模式
被误读的设计模式
125 0
被误读的设计模式