erlang与设计模式(二)——工厂、抽象工厂、建造者

Russell-X-Shanso

工厂模式、抽象工厂模式、建造者模式,均为创建类模式,其共有的设计思路主要在于根据情况理清并封装创建流程(创建进程、创建gen_server、组建record或maps等复合数据结构等)、解耦、定向扩展等等。

(注:由于这三个创建类模型解决的问题近似,面向对象语言中的解决方式也较为近似,因此我们放在一起来讨论;)

3、工厂模式( Factory Method )

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

(2)类型:创建类模式;

(3)面向对象式实现方式:通常利用类的多态特性实现,工厂子类中封装创建方式,通过父类\抽象类\接口暴露出的公用方法进行调用并实例化所需的产品对象;

(4)优点及意义:解耦、封装;

4、抽象工厂模式( Abstract Factory )

(1)意图: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

(2)类型:创建类模式;

(3)面向对象式实现方式:在工厂模式的基础上,将工厂与产品间的关联抽象化,使每个工厂对应整个产品族;

(4)优点及意义:更灵活、强大与复杂地协调工厂与产品线;

5、建造者模式(Builder)

(1)意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

(2)类型:创建类模式;

(3)面向对象式的实现方式:将继承了相同接口的生成器(Builder)对象传入导演(Director)类;

(4)优点及意义:解耦;

这三种模式在erlang的应用场景下都可以用于池化、任务构建、多种类目标创建等;erlang可以利用回调或自定义behaviour来比较轻松地实现这些理念的简单形态及复杂形态(请注意是实现类似的理念,而并非用erlang去复制面向对象的概念性结构)

我先用举例的方式简述这三种模式的理念原理和区别:

比如说你开了一家餐馆,你有三个厨子:中餐厨师、西餐厨师、韩国厨师(厨师为工厂,料理就是产品了)

某些新上手的技术人员的习惯,是把所有的生产集中在一个模块甚至一个方法中实现,在这个例子里,类似于你作为一个餐馆老板亲自去开三个灶去做所有菜,东边撒点盐再西边倒点醋,然后点菜端盘子全都你一个人来,这样应付一个小餐馆的话问题还不明显,但如果你想把事情做好做大,显然是施展不开的。

ok,你意识到了分模块的重要性,但仍然拒绝使用总结出合理的方案进行模式化管理,客人无论点什么你随便指定个厨师就开始做,经过一段时间,菜单也调整了几批后,厨师们责任不明,做菜手法混乱,可能把鸭子腌成了泡菜,谁知道呢……尤其让你无法容忍的是,你不知道让客人崩溃的一道带头发(bug)的菜是哪个厨师在什么地方做出来的;

再让我们看一下上面介绍的几种模式:

在工厂模式下,如果你的餐馆只做烤鸭、牛排、泡菜几种经典菜式,于是你指定中餐厨师做烤鸭,西餐厨师做牛排, 韩国厨师做泡菜,各干各的,分工明确,当客人点其中一道的时候你指定对应的厨师进行烹饪即可,这种情况下你如果新添一位厨师和对应的菜品,只需要加一个产品和其对应的工厂,并在主流程中加入对应的分支就可以了——虽然代码显得多了一些,但几乎不用去考虑过多复杂的变化,其它的厨师甚至感觉不到这个变化,不必担心相互影响,珍爱系统健康,远离难以定位的bug;

在抽象工厂模式下,你的餐馆提供各种鸭、牛、蔬菜类料理,中餐厨师做烤鸭、西红柿炖牛肉、雕萝卜;西餐厨师做鸭肝、牛排、蔬菜沙拉,而韩国厨师只会做泡菜,做鸡和牛的时候都做不出来;这个时候客人选择更多,需要某种风格的某种食材的料理时可以选择对应的厨师去做对应的食材的料理;

在创建者模式下,你的餐馆提供了三位厨师三种风格的套餐,然后问客人(Director)需要指定哪位厨师为其服务,当客人指定厨师后,指挥该名厨师为其烹饪自己喜欢的一系列料理;

具体到erlang领域,我们可以这样总结一下三者的特点:

工厂模式重视创建过程的封装,可以有助于你在情况不是很复杂的情况简单地进行分支控制,当代码量庞大时,把各自的创建过程细致封装有助于整理思路;系统初始设计时可以先简单设计为工厂模式,随着业务的逐渐清晰与扩大,复杂度提升,可根据适合的情况将局部改造为建造类的其它模式;

抽象工厂模式建立了一个多维度的组合对应创建模型,同样是把创建过程封装在不同的方法或模块中,同样用于解耦与思路整理;

创建者模式则更倾向于通过系列的回调进行动作的处理,把单个动作进行封装,与多个动作的组合方式实现逻辑分离;

再具体一些,我们代码说明;

(注:面向对象语言中,工厂与产品通常使用抽象类与类继承实现,限于erlang的理念有所不同,我暂用模块和行为来进行模拟,诸君可活用,例如使用gen_server实现工厂,将任务或进程作为产品来进行生产,引用这些理念进行解耦等等)

(再注:为了说明结构,代码显得有些冗余,有脱了裤子放那啥的嫌疑,在实际运用中可以将冗余部分去掉,灵活运用,还是那句话,重在理念)

先上三个厨师(工厂| 建造者)的代码,为了对三种模式进行比较,在这一系列例子中使用了同样的厨师模块,同时使用callback方式对应三种模式的处理方式,请结合各模式的对应方法进行理解:

中国厨师(chinese_cook.erl):

 1 -module(chinese_cook).
 2 -author("Russell-X-Shanso").
 3
 4 -behaviour( factory ).
 5 -behaviour( abstract_factory ).
 6 -behaviour( builder ).
 7
 8 %% API
 9 -export([ do_best_food/0 ]).
10 -export([ do_duck/0, do_beef/0, do_vegetables/0 ]).
11 -export([ do_breakfast/0, do_lunch/0, do_diner/0 ]).
12
13 %%%%%%%%%%%%%%%%%%%%%%%
14 %% factory callback
15 do_best_food()->
16     { ok, <<"Roast Duck">> }.
17
18 %%%%%%%%%%%%%%%%%%%%%%%
19 %% abstract_factory callback
20 do_duck() ->
21     { ok, <<"Roast Duck">> }.
22
23 do_beef() ->
24     { ok, <<"Tomato beef stew">> }.
25
26 do_vegetables() ->
27     { ok, <<"Carve radish">> }.
28
29 %%%%%%%%%%%%%%%%%%%%%%%
30 %% builer callback
31 do_breakfast() ->
32     { ok, <<"Carve radish">> }.
33
34 do_lunch() ->
35     { ok, <<"Tomato beef stew">> }.
36
37 do_diner() ->
38     { ok, <<"Roast Duck">> }.

西餐厨师(western_cook.erl):

 1 -module(western_cook).
 2 -author("Russell-X-Shanso").
 3
 4 -behaviour( factory ).
 5 -behaviour( abstract_factory ).
 6 -behaviour( builder ).
 7
 8 %% API
 9 -export([ do_best_food/0 ]).
10 -export([ do_duck/0, do_beef/0, do_vegetables/0 ]).
11 -export([ do_breakfast/0, do_lunch/0, do_diner/0 ]).
12
13 %%%%%%%%%%%%%%%%%%%%%%%
14 %% factory callback
15 do_best_food()->
16     { ok, <<"Duck liver">> }.
17
18 %%%%%%%%%%%%%%%%%%%%%%%
19 %% abstract_factory callback
20 do_duck() ->
21     { ok, <<"Duck liver">> }.
22
23 do_beef() ->
24     { ok, <<"Beefsteak">> }.
25
26 do_vegetables() ->
27     { ok, <<"Salad">> }.
28
29 %%%%%%%%%%%%%%%%%%%%%%%
30 %% builer callback
31 do_breakfast() ->
32     { ok, <<"Salad">> }.
33
34 do_lunch() ->
35     { ok, <<"Duck liver">> }.
36
37 do_diner() ->
38     { ok, <<"Beefsteak">> }.

韩国厨师(korea_cook.erl):

 1 -module(korea_cook).
 2 -author("Russell-X-Shanso").
 3
 4 -behaviour( factory ).
 5 -behaviour( abstract_factory ).
 6 -behaviour( builder ).
 7
 8 %% API
 9 -export([ do_best_food/0 ]).
10 -export([ do_duck/0, do_beef/0, do_vegetables/0 ]).
11 -export([ do_breakfast/0, do_lunch/0, do_diner/0 ]).
12
13 %%%%%%%%%%%%%%%%%%%%%%%
14 %% factory callback
15 do_best_food()->
16     { ok, <<"Pickle">> }.
17
18 %%%%%%%%%%%%%%%%%%%%%%%
19 %% abstract_factory callback
20 do_duck() ->
21     { error, <<"Sorry, This cook Can‘t cook duck">> }.
22
23 do_beef() ->
24     { error, <<"Sorry, This cook can‘t cook beef">> }.
25
26 do_vegetables() ->
27     { ok, <<"Pickle">> }.
28
29 %%%%%%%%%%%%%%%%%%%%%%%
30 %% builer callback
31 do_breakfast() ->
32     { ok, <<"Pickle">> }.
33
34 do_lunch() ->
35     { ok, <<"Pickle">> }.
36
37 do_diner() ->
38     { ok, <<"Pickle">> }.

然后是工厂模式:

 1 -module(factory).
 2 -author("Russell-X-Shanso").
 3
 4 %% API
 5 -export([ cook1/1, cook2/1 ]).
 6
 7 -callback do_best_food() -> { ok, binary() }.
 8
 9 cook1( Style )->
10     { ok, Food } = case Style of
11         chinese -> chinese_cook:do_best_food();
12         western -> western_cook:do_best_food();
13         korea -> korea_cook:do_best_food()
14     end,
15     io:format( "Food is:~p~n", [ Food ] ).
16
17
18 cook2( Style )->
19     Cook = case Style of
20                chinese -> chinese_cook;
21                western -> western_cook;
22                korea -> korea_cook
23            end,
24     { ok, Food } = apply( Cook, do_best_food, [] ),
25     io:format( "Food is:~p~n", [ Food ] ).

上面例中用cook1/1与cook2/1两种风格的代码实现了工厂对于产品的生产;

执行结果如下:

抽象工厂模式:

 1 -module(abstract_factory).
 2 -author("Russell-X-Shanso").
 3
 4 %% API
 5 -export([ cook/2 ]).
 6
 7
 8 -callback do_duck() -> { ok, binary() } | { error, ErrorInfo :: binary() }.
 9 -callback do_beef() -> { ok, binary() } | { error, ErrorInfo :: binary() }.
10 -callback do_vegetables() -> { ok, binary() } | { error, ErrorInfo :: binary() }.
11
12 cook( Style, Material ) ->
13     Cook = case Style of
14                chinese -> chinese_cook;
15                western -> western_cook;
16                korea -> korea_cook
17            end,
18     Action = case Material of
19                  duck -> do_duck;
20                  beef -> do_beef;
21                  vegetables -> do_vagetables
22              end,
23     case apply( Cook, Action, [] ) of
24         { ok, Food } -> io:format( "Food is:~p~n", [ Food ] );
25         { error, ErrorInfo } -> io:format( "Oops:~p~n", [ ErrorInfo ] )
26     end.

执行效果:

建造者模式:

 1 -module(builder).
 2 -author("Russell-X-Shanso").
 3
 4 %% API
 5 -export([ cook/1 ]).
 6
 7 -callback do_breakfast() -> { ok, binary() } | { error, ErrorInfo :: binary() }.
 8 -callback do_lunch() -> { ok, binary() } | { error, ErrorInfo :: binary() }.
 9 -callback do_diner() -> { ok, binary() } | { error, ErrorInfo :: binary() }.
10
11 cook( Style ) ->
12     Cook = case Style of
13                chinese -> chinese_cook;
14                western -> western_cook;
15                korea -> korea_cook
16            end,
17     director( Cook ).
18
19
20 director( Cook ) ->
21     { ok, Breakfast } = apply( Cook, do_breakfast, [] ),
22     io:format( "Breakfast: ~p~n", [ Breakfast ] ),
23     { ok, Lunch } = apply( Cook, do_lunch, [] ),
24     io:format( "Lunch: ~p~n", [ Lunch ] ),
25     { ok, Dinner } = apply( Cook, do_diner, [] ),
26     io:format( "Dinner: ~p~n", [ Dinner ] ).

执行效果如下:

希望代码和前面的描述已经充分说明了问题,最后再总结一圈:

(1)工厂模式中有几个目标产品就开几条产品线,重视简单粗暴清晰整洁;

(2)抽象工厂是复杂化了的工厂模式,能够矩阵化地管理产品线,重视整理复杂的流程;

(3)建造者与前两者不同的是更注重将扁平的创建流程分层归纳;在这一角度来说,建造者在erlang中能够更好地利用语言特性,以及用更丰富的方式进行实现不同类型的建造方式;

(4)工厂模式与抽象工厂模式的产品是体系分支的一个结果,而建造者的产品是由若干子产品通过导演(Director)进行一系列调用形成的按一定方式组合好的子产品组,在上例中就是全天的套餐;

(5)建造者模式适合通过独立的动作+预设的流程来组建复杂的结构,例如组建树形结构、组建json或xml;

(6)进行扩展时,工厂模式增加产品时需要增加一条产品线,即一个产品与配套的工厂,上例中每增加一道菜意味着增加一个对应的厨师;抽象工厂可以扩展产品或工厂两个维度,但每个维度的扩展都需要同时一一对应另一个维度进行扩展,上例中增加一个厨师意味着要为这个厨师实现三道菜的制作流程,而增加一样食材意味着所有的厨师都对这个食材进行处理;而建造者模式相对灵活,可以自由地增加菜品和调整套餐的组合方式而不对另一维度造成影响,上例中,如果一个客人点了十份早餐,只需要增加一个导演方法,连续十次要求某厨师制作早餐就可以了,同理如果增加夜宵去丰富未来的菜单需求的话,也对当前已存在的套餐不造成任何影响 。

时间: 2024-08-12 17:51:26

erlang与设计模式(二)——工厂、抽象工厂、建造者的相关文章

设计模式学习03—抽象工厂模式

1.动机与定义 工厂模式中,一个工厂仅仅能提供一个或一类产品,当产品种类较多,形成产品系列(比方我们要创建跨平台的button,菜单,文本框等等一系列GUI控件: 单纯使用工厂模式会产生大量工厂,并且后期维护也不方便,我们能够从产品中找到规律,假设产品等级相对固定,以后仅仅会新增产品族,那么我们就能够把整个产品族放到一个工厂创建,以后新增其它系统产品族也很方便,例如以下图: 这样的模式就是抽象工厂,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则须要面对多个产品等级结构,一个工厂等级结构能

设计模式C++实现——抽象工厂模式

模式定义: 抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类. 抽象工厂允许客户使用抽象的接口来创建一组相关产品,而不需要知道实际产出的具体产品时什么.这样一来,客户就从具体的产品中被解耦了. 模式结构: 举例: 数据库访问程序设计,不同的数据库访问方式可能不一样,为了抽象对对不同数据库的访问,可以将数据库隐藏起来,提供统一的访问方式,用多态进行实现. UML设计: 编程实现及执行结果: #include <iostream> using namespace st

设计模式(二)抽象工厂模式

抽象工厂模式 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类.每个生成的工厂都能按照工厂模式提供对象. 介绍 意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 主要解决:主要解决接口选择的问题. 何时使用:系统的产品有多于一个的产品族,而系统只消费其

创建型设计模式(二)抽象工厂模式

一.一句话背景 假如我是一个汽车经销商,那我不光是卖车,也得卖相关的配件吧,比如润滑油,轮胎之类的,那我能不能整一个抽象的工厂,我需要啥直接告诉这个抽象工厂,具体生产哪一类产品让这个抽象工厂自己去对接,这样我不管需要啥都只用找这个抽象工厂的负责人就可以,而不用找每一类具体产品工厂的负责人,我可就省事多了~ 二.使用场景 调用一整套产品族的场景. 如:参加不同类型的抽奖活动,QQ换皮肤 三.模型分析 生产流水线:接口,定义生产具体产品的方法 具体产品的生产流水线:类,定义生产具体特征(颜色,品牌等

设计模式解析(二)——抽象工厂模式

对抽象工厂模式的理解,主要问题在与工厂模式的区别 仅就定义图和继承关系来看,二者基本上没有区别 但两个模式的定义给出了非常清晰的解释,简单的说,他们所关注的问题层次完全不同 抽象工厂的定义 为创建一组相关性目标实例提供接口而无须指定具体目标类 (Provide an interface for creating families of related or dependent objects without specifying their concrete classes.) 结构示例图 工厂

java23种设计模式(二)抽象工厂模式

我们接着上一章的工厂方法模式继续学习一下抽象工厂模式. 抽象工厂模式:在工厂模式中,如果有多个产品,则就是抽象工厂模式. 例子: 有一个工厂开了两个子公司,专门用来生产电脑配件键盘和鼠标,一个是联想工厂则生产联想的产品,另一个是宏碁工厂则生产宏碁的产品. 代码如下: 1.工厂接口(可以生产鼠标和键盘) public interface Factory { // 生产鼠标 Mouse createMouse(); // 生产键盘 KeyBoard createKeyBoard(); } 2.联想工

深入浅出设计模式 ------ Abstract Factory(抽象工厂)

一. 定义  提供一个创建一系列相关或相互依赖对象的接口, 而无需指定它们的具体类. 二. 结构 三. 参与者 Product (Fruit和Leaf两个抽象产物) : 抽象生产者接口: TreeFruit(树上的水果). TreeLeaf(树上的叶子). GroundFruit(地上的水果). GroundLeaf(地上的叶子) : 实际的自然产物: NatureFactory: 抽象的自然制造工厂, 用于生产水果和叶子: TreeFactory. LeafFactory : 实际的自然制造工

大话设计模式C++版——抽象工厂模式

前面说过,简单工厂模式是最基础的一种设计模式,那以工厂命名的设计模式就是23种设计模式中最多的一种,他们一脉相承,一步一步进化而来,这里就是其中的最后一种--抽象工厂模式(Abstract Factory),其是在工厂方法模式的基础上改进而来,如果没有弄明白工厂方法模式的同学请先观看<大话设计模式C++版--工厂方法模式>. 为什么会有抽象工厂模式?抽象工厂模式是简单工厂模式缺陷的终极解决方式么?NO,抽象工厂模式并不是为了解决简单工厂模式的缺陷而活着,它是因为有新的使命而诞生. 一个简单的例

23种设计模式[3]:抽象工厂模式

一.简单工厂模式(静态工厂方法,不属于23种GOF设计模式之一) 定义:定义一个用于创建产品对象的方法,由该工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例. 类型:创建类模式 public interface SmsService { void sendSms(); } public class MontnetsService implements SmsService { @Override public void sendSms() { Syste

一天一个设计模式——Abstract Factory抽象工厂模式

一.模式说明 前面学习了工厂方法(Factory Method)模式.在工厂方法模式中,在工厂方法模式中,父类决定如何生成实例,但并不决定所要生成的具体类,具体的处理交由子类来处理.这里学习的抽象工厂方法模式中,抽象工厂使用抽象的零件组装成抽象的产品.即使用包含特定的方法接口零件,将零件组装成抽象产品. 二.模式类图: 上面的类图中包含两个包:包含抽象工厂,抽象零件,抽象产品的类所在的包以及具体工厂实现类的包. 三.代码示例 1.Item类: package com.designpattern.