设计模式:用实际案例讲解工厂模式

工厂模式有啥用啊,我的项目没使用工厂模式也照样运行

这是我听过最令人哭笑不得的吐槽,这个程序猿的头发不知道有没有被自己薅秃

的确,项目中不使用工厂模式并不会影响项目的运行

但是,当项目后期需要二次开发时,代码的维护和修改的复杂度,绝对能让你恨不得把自己头发都薅秃

下面我们就来盘一盘工厂模式能解决哪些问题

简单工厂模式

实际案例

假如客户有这样一个需求,做一个用户订购手机来玩游戏的项目

项目中可以生产华为和小米的手机,生产的手机只能用来玩游戏,用户可以通过京东和淘宝来订购手机

需求中的一个前置条件是手机只能用来玩游戏,不能做别的事情。这就类似于一个规范,所有的手机都要遵守这个规范。

制定规范是java中接口的特性,所以我们要定义一个接口基类,叫做Phone,里面有一个玩游戏的方法play()。

还要有华为手机和小米手机的类,分别叫做Huwei和Xiaomi,这两个手机类要遵循手机只能用来玩游戏这个规范,所以它们要实现Phone类,并完成play()方法

用java代码实现分别如下

phone基类

publicinterfacePhone{

voidplay();

}

华为手机类Huawei

publicclassHuaweiimplementsPhone{

@Override

publicvoidplay(){

System.out.println("华为手机玩游戏");

}

}

小米手机类Xiaomi

publicclassXiaomiimplementsPhone{

@Override

publicvoidplay(){

System.out.println("小米手机玩游戏");

}

}

用户可以通过京东和淘宝来订购手机,所以还要有京东和淘宝的类,分别叫做Jingdong和Taobao。类里面各有一个订购方法叫做order(),可以根据用户的喜好来订购不同的手机

如果用户喜欢华为则订购华为手机来玩游戏,如果用户喜欢小米则订购小米手机来玩游戏,其他的用户就不能玩游戏

因为京东类和淘宝类的代码逻辑一摸一样,这里只贴一下京东类的代码

publicclassJingdong{

publicvoidorder(String type){

Phone phone =null;

if("huawei".equals(type)) {

phone =newHuawei();

phone.play();

}elseif("xiaomi".equals(type)) {

phone =newXiaomi();

phone.play();

}else{

System.out.println("暂不支持");

}

}

}

这样我们就实现了客户的需求,而且没有使用任何设计模式。

项目虽然可以完美的运行,但是有一个问题值得我们思考,假如又增加了苹果手机,这时候我们的代码该怎么修改

首先,我们肯定是要增加苹果手机类IPhone,并且实现Phone基类

京东类中订购方法的逻辑需要做出相应的修改

淘宝类中订购方法的逻辑也需要做出相应的修改,修改的地方和京东类一模一样。这时我们就发现同样的代码需要修改两次。

如果订购类有很多,除了京东、淘宝,还有拼多多、微信商城等等。那就意味着同样的代码不止要修改两次,有多少个订购类就需要修改多少次

这个工作量是很大的,而且极其容易出错,就问你头秃不头秃

这时候就需要使用简单工厂模式来优化我们的代码

简单工厂模式就是创建一个工厂类,由这个类来封装实例化对象的行为

套用到这个例子中就是:创建一个工厂类,由工厂类来封装创建手机的逻辑。订购类从工厂类中获取手机对象直接使用,不再关心手机创建的过程

工厂类PhoneFactory用代码实现就是这样

publicclassPhoneFactory{

publicstaticPhonecreatePhone(String type){

Phone phone =null;

if("huawei".equals(type)) {

phone =newHuawei();

}elseif("xiaomi".equals(type)) {

phone =newXiaomi();

}elseif("apple".equals(type)) {

phone =newIPhone();

}else{

System.out.println("暂不支持");

}

returnphone;

}

}

京东、淘宝等订购类从工厂类里面获取手机对象后直接使用,不再关心手机的创建过程(京东和淘宝类的代码一样,这里只贴京东类的代码)

publicclassJingdong{

publicvoidorder(String type){

Phone phone = PhoneFactory.createPhone(type);

if(phone !=null) {

phone.play();

}

}

}

这就实现了简单工厂模式,以后再需要增加手机型号时只需要修改工厂类就行了,其他代码不用修改

简单工厂模式总结

简单工厂模式就是创建一个工厂类,根据传入的参数类型来创建具体的产品对象,并返回产品对象的实例

主要适用于调用者不知道应该创建哪个具体的对象,只能根据传入的条件返回相应对象的场景

比如案例中,订购类是不知道要创建哪个手机对象的,只能根据用户提供的条件才能知道需要哪个手机对象

简单工厂模式的好处在于将对象的创建过程和使用过程进行解耦,减少新增具体产品时修改代码的复杂度

就像上面的例子一样,对象的创建过程由工厂类负责,订购类不需要关心对象是怎么创建的,只需要从工厂类获取对象来使用即可

当需要增加手机对象时,只需要修改工厂类,而不需要对每一个订购类进行修改

简单工厂模式的缺点在于每次新增具体产品时,都需要修改工厂类,这违背了设计模式中的开闭原则。而且当具体的产品比较多时,工厂类的if-else判断就会比较多,代码不美观

工厂方法模式

实际案例

基于刚才用户订购手机玩游戏的需求,我们稍微改动一下。

为了实现精准营销,京东、淘宝等商城分别上线了华为专卖店、小米专卖店和苹果专卖店

当用户进入华为专卖店,就默认用户要订购华为手机;当用户进入小米专卖店,就默认用户要订购小米手机;当用户进入苹果专卖店,就默认用户要订购苹果手机

这个需求用刚才我们讲的简单工厂模式也可以实现

但是简单工厂的缺点也很明显,当新增粉丝类型时需要修改工厂类,当粉丝类型过多时工厂类的逻辑就会比较繁杂

比如新增了vivo粉丝,工厂类就需要新增判断条件去创建vivo手机对象;新增了oppo手机,工厂类就要新增判断条件去创建oppo手机对象。一直不断的新增下去的话,就会导致工厂类的中的判断过多,代码很长,后期不容易维护

而且,简单工厂模式是适用于调用者不知道应该创建哪种对象的场景。

在这个需求中,京东等订购类中为不同的粉丝提供了专卖店,假如专卖店是订购类中的一个方法的话,在专卖店这个方法中是知道应该创建什么样的对象的。比如,在华为手机的订购方法中,是知道要创建华为手机对象的

所以,这个需求可以用工厂方法模式来实现

工厂方法模式和简单工厂模式相似,也需要有一个工厂类。不过在工厂方法模式中,工厂类不再负责创建对象。因为在每个订购方法中已经知道应该创建哪个手机对象,所以创建对象的逻辑下沉到订购类的方法中

工厂类只负责制定规范,来约束每个产品的具体行为,所以工厂类是一个接口基类。每个对应的产品需要有一个自己的工厂,来继承这个基类,达到约束自身行为的目的

这个需求中工厂基类规定每个工厂只能有一个方法,这个方法的作用就是创建手机对象

工厂基类PhoneFactory用代码实现

publicinterfacePhoneFactory{

PhonecreatePhone();

}

华为工厂类用代码实现

publicclassHuaweiFactoryimplementsPhoneFactory{

@Override

publicPhonecreatePhone(){

returnnewHuawei();

}

}

小米工厂类用代码实现

publicclassXiaomiFactoryimplementsPhoneFactory{

@Override

publicPhonecreatePhone(){

returnnewXiaomi();

}

}

在订购类中,不同的订购方法调用不同的工厂获取对象。比如京东订购类的代码如下(淘宝订购类处理逻辑类似,这里不再贴淘宝类的代码

publicclassJingdong{

// 华为粉丝订购华为手机

publicvoidorderHuawei(){

PhoneFactory phoneFactory =newHuaweiFactory();

Phone phone = phoneFactory.createPhone();

phone.play();

}

// 小米粉丝订购小米手机

publicvoidorderXiaomi(){

PhoneFactory phoneFactory =newXiaomiFactory();

Phone phone = phoneFactory.createPhone();

phone.play();

}

}

这样我们就用工厂方法模式实现了为不同粉丝订购不同手机的需求

工厂方法模式总结

工厂方法模式是定义一个工厂接口基类,基类中定义一个创建产品的抽象方法。每个产品需要有自己的工厂来实现这个基类,并完成创建对应产品实例的方法,由具体的产品调用该方法来创建对象

它主要适用于调用者已经明确知道需要创建哪一个具体产品的场景

比如,针对华为的粉丝,已经明确知道要创建华为的手机产品

工厂方法模式的优势在于完全符合了开闭原则,在新增产品时不需要再改动已存在的代码,使工厂类和产品类的代码完全解耦,更利于程序的扩展

他的缺点也很明显,当新增产品时,需要同时新增产品类和工厂类,导致系统中的类是成对增加,增加了系统的复杂度

抽象工厂模式

实际案例

基于工厂方法模式的案例,我们再进一步扩展

用户不单单想订购手机来玩游戏,还想订购ipad和电脑

可以用刚才讲的工厂方法模式来实现:我们不仅需要提供手机工厂的基类,还需要提供ipad工厂基类和电脑工厂基类,并且为每个工厂基类提供具体的工厂实现类

订购类方法中,根据不同的需求来创建不同的产品供用户使用

这样实现的代码没有问题,但是不符合我们真实开发中的业务场景

在实际业务场景中,京东商城的华为专卖店想要订购手机不需要到华为公司的手机部门去订购吧?想要订购ipad不需要到华为公司的ipad部门订购吧?想要订购电脑也不需要到华为公司的电脑部门订购吧?

京东商城的华为专卖店应该只负责和华为公司对接,和具体的业务部门没关系。专卖店想要订购某个产品去告诉华为公司,由公司去给具体的业务部门沟通

所以,从实际的使用场景出发,我们的代码应该这样设计

不再使用单独的手机工厂、ipad工厂和PC工厂,而是把同一个厂家作为工厂,由工厂分别创建不同的产品

Factory基类实现代码如下

publicinterfaceFactory{

PhonecreatePhone();

IPadcreateIPad();

PCcreatePC();

}

华为工厂类实现代码如下

publicclassHuaweiFactoryimplementsFactory{

@Override

publicPhonecreatePhone(){

returnnewHuaweiPhone();

}

@Override

publicIPadcreateIPad(){

returnnewHuaweiIPad();

}

@Override

publicPCcreatePC(){

returnnewHuaweiPC();

}

}

小米工厂实现代码如下

publicclassXiaomiFactoryimplementsFactory{

@Override

publicPhonecreatePhone(){

returnnewXiaomiPhone();

}

@Override

publicIPadcreateIPad(){

returnnewXiaomiIPad();

}

@Override

publicPCcreatePC(){

returnnewXiaomiPC();

}

}

在京东订购类中,我们只需要创建对应的工厂对象,由工厂对象创建不同的产品

publicclassJingdong{

// 华为粉丝订购手机、ipad、电脑

publicvoidorderHuawei(){

Factory factory =newHuaweiFactory();

Phone phone = factory.createPhone();

phone.play();

IPad ipad = factory.createIPad();

ipad.play();

PC pc = factory.createPC();

pc.play();

}

// 小米粉丝订购手机、ipad、电脑

publicvoidorderXiaomi(){

Factory factory =newXiaomiFactory();

Phone phone = factory.createPhone();

phone.play();

IPad ipad = factory.createIPad();

ipad.play();

PC pc = factory.createPC();

pc.play();

}

}

这样我们就用抽象工厂模式实现了用户订购手机、ipad和电脑的需求

抽象工厂模式总结

抽象工厂模式是将具有一定共性的产品封装到一块,由工厂类分别为这些产品提供创建对象的方法,调用者可以根据不同的需求调用工厂类的具体方法来获得产品实例

比如案例中华为的手机、ipad和电脑都属于华为公司产品,所以可以由华为工厂类来负责分别创建不同的对象

它的优势在于将具有一定共性的产品集合封装到一起,在实际开发中更符合具体的业务场景

他的缺点就是降低了系统的扩展性,当新增产品时需要修改工厂类,在工厂类的基类和实现类中都需要增加对应的方法

比如说,用户也想订购VR眼镜来玩游戏。那么工厂基类中需要增加创建VR眼镜的方法,所有的工厂实现类中都需要增加对该方法的实现,系统扩展性比较差

-- 文章来自赫连小伍公众号

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容