关于设计模式的思考

为什么叫设计模式 什么是设计模式

设计模式最初是被 GoF 于1995年提出的。GoF 全称是Gang of Four(四人帮),即 Erich Gamma,Richard Helm,Ralph Johnson 和 John Vlissides。他们四人于1995年出版了一本书 <Design Patterns: Elements of Reusable Object-Oriented Software>(中文翻译为“设计模式:可复用面向对象软件的基础”),第一次将设计模式提升到理论高度并将之规范化。该书提出了23种经典的设计模式。

设计模式(Design pattern)是一套被反复使用、多数人知晓的、无数工程师实践的代码设计经验的总结,它是面向对象思想的高度提炼和模板化。使用设计模式是为了让代码具有更高的可重用性,更好的灵活性和可拓展性,更易被人阅读和理解。GoF 提到模式有四个基本要素:

  1. 模式名称:助记名,方便讨论、交流、传播;
  2. 问题:该模式是用来解决哪类实际问题,即它的应用场景;
  3. 解决方案:设计的组成部分,它们之间的相互关系及各自的职责和协作方式;
  4. 效果:使用模式能达到的效果,即对使用条件的权衡取舍。

设计模式与生活有什么联系

我一直坚信:程序源于生活,又高于生活。程序的灵魂在于思维的方式,而思维的灵感来源于生活的精彩。互联网是一个虚拟的世界,而程序本身就是对生活场景的虚拟和抽象,每一个模式我都能在生活中找到他的影子。比如,说到状态模式我能想到水有冰、水、气三种状态,而人也有少、壮、老三个不同的阶段;提起中介模式我能立马想到房产中介;看到单例模式,脑海中会即刻浮现心目中的那个她……

设计模式是面向对象的高度抽象和总结,而越抽象的东西越难以理解。本系列文章的目地就是为了降低设计模式的阅读门槛,以生活中的小故事开始,用风趣的方式,由浅入深地讲述每一个模式。让你再次看到设计模式不只是一个模式,还是生活中的一个个小确幸!程序不是冷冰冰的代码,它还有生活的乐趣和特殊意义。

为什么要学设计模式

设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间试验和错误总结出来的。所以不管你是新手还是老手,学习设计模式对你都有莫大的帮助。

学习设计模式的理由有很多,我这里只列出几个最现实的:

  1. 摆脱面试的窘境,不管你是前端工程师还是后端工程师,亦或是全端工程师,设计模式都是不少面试官必问一道题。
  2. 让你的程序设计能力有一个质的提升,不再是写一堆结构复杂,难以维护的烂代码。
  3. 使你对面向对象的思想有一个更高层次的理解。

如何进行学习

  • 熟悉一门面向对象语言

首先,你至少要熟悉一门面向对象的计算机语言。如果没有,请根据自己学习、爱好,或希望从事的工作,先选择一门面向对象语言(C++,Java,Python,Go 等都可以)进行学习和实战,对抽象、继承、多态、封装有一定的基础之后,再来看本系列的文章。

  • 了解 Python 的基本语法

对 Python 基本语法有一个简单了解。Python 语法非常简单,只要你有一定的编程语言基础,通过下文的介绍很快就能理解的。

  • 学会阅读 UML 图

UML(Unified Modeling Language)称为统一建模语言或标准建模语言,是面向对象软件的标准化建模语言。UML 规范用来描述建模的概念有:类(对象的)、对象、关联、职责、行为、接口、用例、包、顺序、协作以及状态。

UML 类图表示不同的实体(人、事物和数据)如何彼此相关;换句话说,它显示了系统的静态结构。想进一步了解类图中的各种关系,可参考以下文章: UML 类图关系大全 UML 类图关系(泛化 、继承、实现、依赖、关联、聚合、组合)

  • 阅读本系列文章

通过阅读本系列文章,以轻松愉快的方式学习设计模式和编程思想。本系列文章每一章都是单独成文,可从任意一篇文章开始。

为什么选择Python

虽然说设计模式与编程语言没有关系,它是对面向对象思想的灵活应用和高度概括,你可以用任何一种语言来实现它,但总归是需要用一种语言进行举例的。本系列文章的所有示例代码均使用 Python 编写,为什么选择 Python,主要基于以下两个原因。

弥补市场空缺

设计模式于1995年被 GoF 提出,被广泛应用于热门的面对象语言。目前用 Java、C++ 描述的设计模式的书籍和资料已经非常多了,但用 Python 来描述的真是太少了。我在当当上搜索了一下“Python 设计模式”,只有那零星的几本书。而作为已经挤进 Top4 的 Python 语言,这明显是不够的。Python 已经越来越成熟,也越来越多地被使用,作为一个有技术追求的 IT 人有必要了解一下基于 Python 代码设计。

大势所趋,Python 已然成风

C 语言诞生于1972年,确随着 Unix 的诞生才深深植根于各大操作系统;

C++ 诞生于1983年,确因微软的可视化桌面操作系统才得以广泛传播;

Java 诞生于1995年,确因互联网的迅速崛起才变得家喻户晓;

Python 诞生于1991年,而下一场技术革命已然开始,AI 时代已然成风。在 AI 领域中已经被广泛使用的 Python 必将成为下一个时代的第一开发语言!

最热门的 AI 开源框架 PyTorch 和 TensorFlow 都已经采用了 Python 作为接口和开发语言。除此之外,还有一堆 AI 相关的框架库,也都纷纷采用,如 AIMA、pyDatalog、SimpleAI、PyBrain、PyML 等。

作为这么一门有前途的语言,必然是要去学习和使用的。

简单的Python基础

(这一部分内容,如果你已经熟悉 Python,可直接跳过。)

Python 的特点

Python 崇尚优美、清晰、简单,是一个优秀并广泛使用的语言。

与 Java 和 C++ 这些语言相比,Python 最大的两个特点是:

  1. 语句结束不用分号“;”。
  2. 代码块用缩进来控制,而不用大括号“{}”。

刚转过来的时候可能会有点不适,用一段时间就好了!

个人觉得,在所有的高级计算机语言中,Python 是最接近人类自然语言的。Python 的语法、风格都与英文的书写习惯非常接近,Python 的这种风格被称为 Pythonic。如条件表达式,在 Java 和 C++ 中是这样的:

int min = x < y ? x : y

而 Python 是这样的:

min = x if x < y else y

有没有觉得第二种方式更接近人类的自然思维?

基本语法

数据类型

Python 是一种弱类型的语言,变量的定义不需要在前面加类型说明, 而且不同类型之间可以方便地相互转换。Python 有五个标准的数据类型:

  1. Numbers(数字)
  2. String(字符串)
  3. List(列表)
  4. Tuple(元组)
  5. Dictionary(字典)

其中 List,Tuple,Dictionary 为容器,将在下一部分介绍。Python 支持四种不同的数字类型:int(有符号整型)、long(长整型)、float(浮点型)、complex(复数)。

每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。

Demo:

age = 18        # intweight = 62.51  # floatname = "Tony"    # stringprint("age:", age)print("weight:", weight)print("name:", name)# 变量的类型可以直接改变age = nameprint("age:", age)

a = b = c = 5# a,b,c三个变量指向相同的内存空间,具有相同的值print("a:", a, "b:", b, "c:", c)print("id(a):", id(a), "id(b):", id(b), "id(c):", id(c))

结果:

age: 18weight: 62.51name: Tonyage: Tonya: 5 b: 5 c: 5id(a): 1457772400 id(b): 1457772400 id(c): 1457772400

常用容器

List(列表)

List(列表)是 Python 中使用最频繁的数据类型,用 [ ] 标识。

列表可以完成大多数集合类的数据结构实现。类似于 Java 中的 ArrayList,C++ 中的 Vector。此外,一个 List 中还可以同时包含不同类型的数据,支持字符、数字、字符串,甚至可以包含列表(即嵌套)。

  1. 列表中值的切割也可以用到变量 [头下标:尾下标] ,就可以截取相应的列表,从左到右索引默认 0 开始,从右到左索引默认 -1 开始,下标可以为空表示取到头或尾。
  2. 加号(+)是列表连接运算符,星号(*)是重复操作。

Demo:

list = ['Thomson', 78, 12.58, 'Sunny', 180.2]
tinylist = [123, 'Tony']print(list)             # 输出完整列表print(list[0])          # 输出列表的第一个元素print(list[1:3])          # 输出第二个至第三个元素print(list[2:])          # 输出从第三个开始至列表末尾的所有元素print(tinylist * 2)      # 输出列表两次print(list + tinylist)  # 打印组合的列表list[1] = 100            # 修改第二个元素的值print(list)              # 输出完整列表list.append("added data")print(list)              # 输出增加后的列表

结果:

['Thomson', 78, 12.58, 'Sunny', 180.2]
Thomson
[78, 12.58]
[12.58, 'Sunny', 180.2]
[123, 'Tony', 123, 'Tony']
['Thomson', 78, 12.58, 'Sunny', 180.2, 123, 'Tony']
['Thomson', 100, 12.58, 'Sunny', 180.2]
['Thomson', 100, 12.58, 'Sunny', 180.2, 'added data']
Tuple(元组)

Tuple(元组)是另一个数据类型,元组用“()”标识,内部元素用逗号隔开。元组不能二次赋值,相当于只读列表,用法与 List 类似。Tuple 相当于 Java 中的 final 数组,C++ 中的 const 数组。

Demo:

tuple = ('Thomson', 78, 12.58, 'Sunny', 180.2)
tinytuple = (123, 'Tony')print(tuple)              # 输出完整元组print(tuple[0])          # 输出元组的第一个元素print(tuple[1:3])          # 输出第二个至第三个的元素print(tuple[2:])          # 输出从第三个开始至列表末尾的所有元素print(tinytuple * 2)      # 输出元组两次print(tuple + tinytuple)# 打印组合的元组# tuple[1] = 100         # 不能修改元组内的元素

结果:

('Thomson', 78, 12.58, 'Sunny', 180.2)
Thomson
(78, 12.58)
(12.58, 'Sunny', 180.2)
(123, 'Tony', 123, 'Tony')
('Thomson', 78, 12.58, 'Sunny', 180.2, 123, 'Tony')
Dictionary(字典)

Dictionary(字典)是 python 中除列表以外最灵活的内置数据结构类型。字典用"{ }"标识,字典由索引(key)和它对应的值 value 组成。相当于 Java 和 C++ 中的 Map。

列表是有序的对象集合,字典是无序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。

Demo:

dict = {}
dict['one'] = "This is one"dict[2] = "This is two"tinydict = {'name': 'Tony', 'age': 24, 'height': 177}print(dict['one'])      # 输出键为'one' 的值print(dict[2])          # 输出键为 2 的值print(tinydict)         # 输出完整的字典print(tinydict.keys())  # 输出所有键print(tinydict.values())# 输出所有值

结果:

This is one
This is two
{'name': 'Tony', 'age': 24, 'height': 177}
dict_keys(['name', 'age', 'height'])
dict_values(['Tony', 24, 177])

类的定义

使用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾,如下实例:

class ClassName:
   '类的帮助信息'   #类文档字符串
   class_suite  #类体

类的帮助信息可以通过 ClassName._doc_ 查看,class_suite 由类成员、方法、数据属性组成。如:

class Test:
    "这是一个测试类"

    def __init__(self):        self.__ivalue = 5

    def getvalue(self):        return self.__ivalue

其中 _init_ 为初始化函数,相当于构造函数。

访问权限:

_foo_:定义的是特殊方法,一般是系统定义名字 ,类似 _init_() 之类的。

_foo:以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import 。

__foo:双下划线的表示的是私有类型(private)的变量,只能是允许这个类本身进行访问了。

类的继承:

继承语法结构

class 派生类名(基类名):
    类体

python 中继承中的一些特点:

  1. 在继承中基类的构造(_init_()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
  2. 在调用基类的方法时,需要使用 super() 前缀。
  3. Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

如果在继承元组中列了一个以上的类,那么它就被称作“多重继承”。

基础重载方法

Python 的类中有很多内置的方法,我们可以通过重写这些方法来实现一些特殊的功能。这些方法有:

Demo 让你顿悟

我们将一段 Java 的代码对应到 Python 中来实现,进行对比阅读,相信你很快就能明白其中的用法。

Java 代码:

class Person {    public static int visited;

    Person(String name, int age, float height) {        this.name = name;        this.age = age;        this.height = height;
    }    public String getName() {        return name;
    }    public int getAge() {        return age;
    }    public void showInfo() {
        System.out.println("name:" + name);
        System.out.println("age:" + age);
        System.out.println("height:" + height);
        System.out.println("visited:" + visited);
        Person.visited ++;
    }    private String name;    protected int age;    public  float height;
}class Teacher extends Person {

    Teacher(String name, int age, float height) {
        super(name, age, height);
    }    public String getTitle() {        return title;
    }    public void setTitle(String title) {        this.title = title;
    }    public void showInfo() {
        System.out.println("title:" + title);
        super.showInfo();
    }    private String title;

}public class Test {    public static void main(String args[]) {
        Person tony = new Person("Tony", 25, 1.77f);
        tony.showInfo();
        System.out.println();
        Teacher jenny = new Teacher("Jenny", 34, 1.68f);
        jenny.setTitle("教授");
        jenny.showInfo();
    }
}

对应的 Python 代码:

class Person:
    "人"
    visited = 0

    def __init__(self, name, age, height):        self.__name = name        self._age = age        self.height = height    def getName(self):        return self.__name    def getAge(self):        return self._age    def showInfo(self):
        print("name:", self.__name)
        print("age:", self._age)
        print("height:", self.height)
        print("visited:", self.visited)
        Person.visited = Person.visited +1class Teacher(Person):
    "老师"

    def __init__(self, name, age, height):        super().__init__(name, age, height)        self.__title = None    def getTitle(self):        return self.__title    def setTitle(self, title):        self.__title = title    def showInfo(self):
        print("title:", self.__title)        super().showInfo()def testPerson():    "测试方法"
    tony = Person("Tony", 25, 1.77)
    tony.showInfo()
    print();

    jenny = Teacher("Jenny", 34, 1.68);
    jenny.setTitle("教授");
    jenny.showInfo();

testPerson()

自己测试一下,会发现结果是一样的:

name: Tonyage: 25height: 1.77visited: 0title: 教授name: Jennyage: 34height: 1.68visited: 1

重要说明

  1. 为了降低程序复杂度,本系列文章中用到的所有示例代码均不考虑多线程安全,望借鉴 Demo 的读者注意。
  2. 本系列所有 Demo 均是在 Python 3.6.3 下编写的,Python 3.0 以上应该都可以正常运行。

作者说:

我一直坚信:程序源于生活,又高于生活。程序的灵魂在于思维的方式,而思维的灵感来源于生活的精彩。

本系列课程我将以全新的方式,从生活中你我的故事开始,由浅入深地逐步阐述设计模式的思想,并抽象出代码模型(骨架)。

原文发布于微信公众号 - CSDN技术头条(CSDN_Tech)

原文发表时间:2018-03-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程微刊

2018年各大互联网前端面试题二(滴滴打车)

3952
来自专栏racaljk

什么是副作用(Side Effect)

副作用(Side Effect)是指函数或者表达式的行为依赖于外部世界。具体可参照Wiki上的定义,副作用是指

1367
来自专栏斑斓

从map函数引发的讨论

只要你用心,一个细小问题可以引起对一系列设计原则的思考与回味。软件设计与开发技能就是这样通过不停“反刍”与思索而磨砺出来的。当然,对一些实践案例进行升华,进而抛...

3769
来自专栏程序员互动联盟

C语言最难啃的三块硬骨头

提到C语言很多初学者都觉得,学到中间就进行不下去了,因为碰到了几个硬骨头死活翻不过去,于是很多人给C语言下结论太难了,太靠近底层了,特别是那几块难啃的骨头,直接...

4145
来自专栏iKcamp

翻译连载 |《你不知道的JS》姊妹篇 |《JavaScript 轻量级函数式编程》- 第 6 章:值的不可变性

原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 第 6 章:值的不可变性 在第 ...

2085
来自专栏机器学习算法与Python学习

Python技巧 101:这17个骚操作你都Ok吗

Python 是一门非常优美的语言,其简洁易用令人不得不感概人生苦短。在本文中,作者 Gautham Santhosh 带我们回顾了 17 个非常有用的 Pyt...

1124
来自专栏Java架构沉思录

聊聊设计模式之工厂方法模式

定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

1113
来自专栏Android知识点总结

1.计算机编程通之字节与数据类型

1477
来自专栏贾老师の博客

浮点数存储格式

1273
来自专栏积累沉淀

Java设计模式(三—四)----工厂模式

Java设计模式 工厂模式 一、引言 二、分类 三、简单工厂模式 四、工厂方法模式 五、抽象工厂模式 六、和工厂方法模式区别 七、总...

1905

扫码关注云+社区

领取腾讯云代金券