零、写在前面
想写设计模式,是因为读完了《漫谈设计模式》,有颇多感触。
将自己对于设计模式的理解记录下来,一方面防止自己遗忘,另一方面也可以给新手们一些入门级的帮助。
如果你要我给你一个看下去的理由的话,那么我只说四个字——浅显易懂。
设计模式本来是一个很简单的东西,但是被专家们一总结一归纳,就变成了一套一套的理论。
我觉得对于新手来说,最重要的是搞懂“是什么”和“为什么”,至于更深层的东西,等你实践得多了自然就懂了。
文中示例所用语言为python,简洁明了。
个人理解难免有些偏差,如果有错误欢迎大家指出,我会及时改正的。
也欢迎大家提出各种意见建议。
重复一遍——浅显易懂。至于到底多浅显易懂,请往下看吧。
要查看本系列所有文章,请点击页面右侧“随笔分类”中的“设计模式”。
一、工厂模式
首先来看存在问题的代码:
# games.py class play_war3: ...... ------------------------ # test.py from games.py import * if game_version == '2.4e': configs = get_config(game_version) game = play_war3(configs) elif game_version == '2.5': configs = get_config(game_version) game = play_war3(configs) else: print "wrong version" game.start() game.play()
大家注意一下,上面分别是两个文件的内容。
games.py模拟的是外部的代码,也就是说对于用户来说是无法更改或者说不会去更改的。
test.py模拟的是用户代码。
解释一下这个场景:
games.py是我们给用户写的一个模块,用户要使用我们这个模块来生成不同版本的游戏并进行玩耍。
使用这个类的时候,用户需要根据不同的游戏版本获取不同的配置,然后创建实例。
大家可以看到,实际上类里面的方法名都是统一的,只是初始化(获取配置)的过程不一样。
这里问题就出现了,如果我们的类有1000种版本的话,用户就需要写1000个if-elif来进行判断,分别进行初始化。
而且,如果我们对类进行了修改,比如我们修改了类的名字,用户的代码也需要进行相应的改动,这很明显是不合适的。
如果真实生活中因为我们的模块变动导致用户需要修改很多代码的话,我想大部分用户都不会继续使用这个模块了。
下面看解决方法:
# games.py class play_war3: ...... class create_game: def __init__(self, game_version): if game_version == '2.4e': return play_war3(get_config(game_version)) elif game_version == '2.5': return play_war3(get_config(game_version)) else: print "wrong version" ------------------------ # test.py from games.py import * game = create_game(game_version) game.start() game.play()
我们新建了一个类create_game,把判断过程和初始化过程都放到了create_game里,这样用户使用的时候只要传入参数就可以生成对应的类。
我们看看这样是否能解决上面提到的两个问题:
- 如果有1000个类怎么办?——我们在create_game中写1000个if-elif就行。
-
如果类进行了改动,比如play_war3改成了play_war4怎么办?——我们在create_game中修改对应的类名就可以。
我们可以看到,无论我们对代码进行什么修改,用户的代码都不用改动。
这种方法就叫做工厂方法,create_game就叫做工厂类。
其实我们可以把工厂类理解成某个内部类对外的一个接口,这个接口将内部类的具体实现和用户使用隔离开来,保证内部类代码的变动不会影响用户代码。
工厂模式的优点:
- 根据输入来生成不同类的实例
- 封装初始化过程
- 缓存对象
最后一点缓存对象实际上和单例模式很像,就是把初始化比较慢的对象或者比较大的对象缓存起来,当用户下次再创建类的时候就可以直接使用。典型的例子就是数据库连接,缓存在create_game类当中就可以了。
二、静态工厂模式
工厂模式虽然很不错,但是还是有一些问题。
我们可以看到。例子中是一个类对应一个工厂类,那么如果我们有1000个类,就需要1000个工厂类!这将导致工厂类的泛滥。
解决方法就是采用静态工厂模式:
# games.py class play_war3: def __init__(self, game_version): if game_version == '2.4e': return do_with_24e(get_config(game_version)) elif game_version == '2.5': return do_with_25(get_config(game_version)) else: print "wrong version" ...... ------------------------ # test.py from games.py import * game = play_war3(game_version) game.start() game.play()
我们把判断和初始化的过程放到了play_war3内部,这样就不会导致工厂类的泛滥了。
三、我的理解
首先说说为什么叫“静态”工厂方法。
这个是因为JAVA当中有static方法,不过Python当中没有这一说。
然后呢,大家看完可能有点迷糊,会觉得在类内部进行判断不是很正常的事吗?
实际上这个例子还是有点太简单了,在一般的工程当中,实例化一个类可能包括很多环节,比如进行一些变量设置、环境配置、初始化其他类等等。
也就是说,工厂模式的核心思想是把创建实例的整个过程都放到了类内部,尽量减少用户的负担。
用户只需要传入必要的参数,并不用关心具体实例化到底要做什么事情。
还要强调的一点就是“接口”。
我们写类的时候,一般来说需要对外暴露统一的接口。接口的作用就是将内部的具体实现和外部的使用隔离开来,这样修改内部代码的时候,可以通过修改接口保证对外的一致性,不用改动用户代码。
接口的思想在设计模式当中是非常基础但是非常重要的,大家一定要好好理解。