有趣的设计模式——从手机的制造过程学习工厂模式


版权声明


开篇语

前不久,在写工厂设计模式时,我还是期望用一个例子来阐述它的原理和应用。可是,当我写完之后才发现:单凭一个示例很难梳理出工厂模式。换句话说,就是之前的套路不好使了。嗯哼,既然原来的方式行不通,那就另辟蹊径:我们从手工打造开始讲起,一步步演变过度到现在的工厂设计模式。

我想能看到这篇博客的人,都会有一部属于自己的手机;它出现于你的裤兜,办公桌,写字台,饭桌,枕头边;当然更多的时候它就在你手掌。既然,大家对这个玩意这么熟悉,我们就用生产手机为例子来学习和了解工厂设计模式。

自己制作手机

我们先利用时光机回退到大概二十年前:你需要一部手机给你在远方的女朋友打电话。但是,你没有手机啊,市面上也没有卖的啊,可是相思之情难以抑制,每当傍晚都会涌上心头。于是,你开始自己造手机。

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class HWMobile{
	
	public HWMobile() {
		System.out.println("自己动手生产一部华为手机,好累啊");
	}
	
	public void call(String number) {
		System.out.println("利用华为手机拨打电话:"+number);
	}
	
	public void sendMessage(String message) {
		System.out.println("利用华为手机发送短信:"+message);
	}	
}

嗯哼,我们自己制造了一部华为手机,虽然颜值不高,但是可以打电话,发短信了!

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {

	public static void main(String[] args) {
		HWMobile hwMobile=new HWMobile();
		hwMobile.call("95279527");
		hwMobile.sendMessage("I miss you");
	}

}

在这里插入图片描述

二话不说,拿起手机给妹子打电话,泡妹的事业又可以继续了;但是累啊!自己动手一点一滴地生产组装一个手机,累得直吐血啊!
在这里插入图片描述

怎么办呢?有没有其他工厂来帮我们生产手机呢?有啊,必须有啊,比如全球最大的代工厂穷士康!

简单工厂模式(Simple Factory)

好了,既然有工厂,那我们就请工厂代劳。

手机接口

利用接口定义手机常用功能例如:打电话和发短信

package com.factoryPattern02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:Mobile接口
 */
public interface Mobile {
	public void call(String number);
	public void sendMessage(String message);
}

标配版手机

该标配版实现Mobile接口。

package com.factoryPattern02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:标配版手机
 */
public class HWStandardMobile implements Mobile {
	
	public HWStandardMobile() {
		
		System.out.println("工厂生产了一部标配版华为手机");
	}

	@Override
	public void call(String number) {
		System.out.println("利用华为手机拨打电话:"+number);
	}

	@Override
	public void sendMessage(String message) {
		System.out.println("利用华为手机发送短信:"+message);
	}

}

高配版手机

该高配版实现Mobile接口。

package com.factoryPattern02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:高配版手机
 */
public class HWProfessionalMobile implements Mobile {
	
	public HWProfessionalMobile() {
		System.out.println("工厂生产了一部高配版华为手机");
	}

	@Override
	public void call(String number) {
		System.out.println("利用华为手机拨打电话:"+number);
	}

	@Override
	public void sendMessage(String message) {
		System.out.println("利用华为手机发送短信:"+message);
	}

}

手机工厂

穷士康说:既然要造手机,我就给你造两个,一个高配版,一个标准版,想用哪个自己挑就是了。好嘞,我们来看看工厂是怎么制造手机的。

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class MobileFactory {
	public final static String STANDARE="standard";
	public final static String PROFESSIONAL="professional";
	public static Mobile createMobile(String type) {
		Mobile mobile = null;
		switch (type) {
			case STANDARE:
				mobile=new HWStandardMobile();
				break;
			case PROFESSIONAL:
				mobile=new HWProfessionalMobile();
				break;
			default:
				break;
		}
		return mobile;
	}
}

作为用户,我们只需要告诉工厂生产什么手机,它就会自动帮我们制造。既然这么方便,那就赶紧试一把吧。

测试

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {

	public static void main(String[] args) {
		Mobile mobile1 = MobileFactory.createMobile("standard");
		Mobile mobile2 = MobileFactory.createMobile("professional");
	}

}

在这里插入图片描述

在此,让工厂生产两部手机,一部高配,一部标配!这样是不是省事好多?爽了吧?是的,你是爽了,舒服了;可是,工厂不愿意了!为啥呢?假设哪天你不愿意用标配和高配的手机了,你想用更高级的钻石级华为手机,那么这个工厂就得进行大的改动:

	switch (type) {
			case STANDARE:
				mobile=new HWStandardMobile();
				break;
			case PROFESSIONAL:
				mobile=new HWProfessionalMobile();
				break;
			default:
				break;
	}

更加确切地说:这个switch语句得新加case了!为了生成新系列的手机就得对工厂进行伤筋动骨的改造,人家穷士康当然不愿意了!再从软件工程的角度来看,这也违背了开闭原则。所以,简单工厂模式并不是真正的设计模式,23种设计模式里并没有它的一席之地。

工厂方法模式(Factory Method)

虽然全球最大的代工厂穷士康不愿意为了新产品大刀阔斧地改造原来的工厂但是也不愿意订单花落他家;生意还是要做,钱还是要赚的。那怎么办?干脆建立一个工厂模型,每当有新产品的制造需求时按照这个模型新建一个工厂就行啦!

所以,在不改变手机接口及其实现类的基础上我们来修改工厂。

手机工厂接口

package com.factoryPattern03;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:手机工厂接口
 */
public interface MobileFactory {
	public abstract Mobile createMobile();
}

标配手机工厂

要点概述:

  • 1、该工厂实现了手机工厂接口。
  • 2、该工厂负责生产标配手机。
package com.factoryPattern03;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:成产标配手机的工厂
 */
public class HWStandardMobileFactory implements MobileFactory {
	@Override
	public Mobile createMobile() {
		HWStandardMobile mobile=new HWStandardMobile();
		return mobile;
	}
}

高配手机工厂

要点概述:

  • 1、该工厂实现了手机工厂接口。
  • 2、该工厂负责生产高配手机。
package com.factoryPattern03;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:成产高配手机的工厂
 */
public class HWProfessionalMobileFactory implements MobileFactory {
	@Override
	public Mobile createMobile() {
		HWProfessionalMobile mobile=new HWProfessionalMobile();
		return mobile;
	}
}

测试

package com.factoryPattern03;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {
	public static void main(String[] args) {
		Mobile mobile = null;
		MobileFactory mobileFactory=null;
		//生产标配手机
		mobileFactory=new HWStandardMobileFactory();
		mobile = mobileFactory.createMobile();
		//生产高配手机
		mobileFactory=new HWProfessionalMobileFactory();
		mobile = mobileFactory.createMobile();
	}
}

在这里插入图片描述

小结

在此,我们对现在的工厂方法模式做一个阶段性的小结:

  • 1、具体产品均实现了自抽象产品接口。
    比如,高配版和标准版的华为手机都implements Mobile

  • 2、具体工厂均实现了抽象工厂接口。
    比如,生产标配手机的工厂和生产高配手机的工厂都implements MobileFactory

  • 3、当有新产品需求时,只需要新建工厂进行生产,而不必去修改原来的已经存在的工厂代码。所以,该方式是符合开闭原则的。如果有新的华为手机(例如比高配版还要牛气的钻石系列)的生产需求,那么再建立一个对应的工厂就行啦!

问题与思考

讲到这里,咋们的工厂方法模式,是不是就可以结束了呢?貌似是可以完结了,但是那些好学的学霸就有疑问了:这种写法是不是太啰嗦了呢?我们真的需要建立这么多具体的工厂类么?

听到学霸的疑问,学渣也开始思考了:与简单工厂模式比起来,工厂方法模式虽然遵守了开闭原则,但是建立了很多具体的工厂类。这样的代码有些臃肿,不便于维护。

听到童鞋们都这么说,穷士康也开始鼓噪了:就是嘛,建这么多工厂多费劲啊,花了我们这么多的本钱!

好了,既然这样的实现大家都觉得不太好;那么该怎么优化呢?请注意我们之前的小结: 具体产品均实现了自抽象产品接口;具体工厂均实现了抽象工厂接口。看来,我们可以在在工厂这方面做做文章,争取一个工厂就可以生产所有的手机。那么,该怎么优化呢?请继续往下看。

带泛型的手机工厂接口

package com.factoryPattern04;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:带泛型的手机工厂接口
 */
public interface MobileAbstractFactory {
	public <T extends Mobile> T createMobile(Class<T> clazz);
}

实现带泛型的手机工厂接口的类

在该具体工厂中采用反射的方式生产不同的手机;从而避免建立众多的工厂。

package com.factoryPattern04;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:实现带泛型的手机工厂接口的类
 */
public class MobileConcreteFactory implements MobileAbstractFactory {
	@SuppressWarnings("unchecked")
	@Override
	public <T extends Mobile> T createMobile(Class<T> clazz) {
		Mobile mobile=null;
		String className=clazz.getName();
		try {
		     mobile=(Mobile) Class.forName(className).newInstance();
		}catch(Exception e) {
			e.printStackTrace();
		}
		return (T) mobile;
	}
}

测试

package com.factoryPattern04;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {
	public static void main(String[] args) {
		MobileAbstractFactory mobileFactory=new MobileConcreteFactory();
		HWStandardMobile mobile1 = mobileFactory.createMobile(HWStandardMobile.class);
		HWProfessionalMobile mobile2 = mobileFactory.createMobile(HWProfessionalMobile.class);
	}
}

在这里插入图片描述

哇哈,我们只用创建一个工厂就可以生产不同的手机!想生产说明手机直接告诉厂商手机类型就行啦!穷士康也高兴坏了,真爽,节约了一大笔资金!

抽象工厂模式(Abstract Factory)

华为手机上市一段时间之后,用户普遍反应:日常生活中的不小心导致手机经常摔坏!穷士康听到这个消息后,就开始琢磨了:给手机配备手机保护套!也就是说:为高配版的手机生产与之对应的真皮保护套;至于标配版的手机就整个塑料材质的保护套。毕竟是生意人啊,想得真细致,太精打细算了!

所以,在不改变手机接口及其实现类的基础上我们来进行相应的修改。

手机保护套接口

package com.factoryPattern05;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:手机保护套接口
 */
public interface PhoneCase {
	public abstract void protectMobile();
}

保护标配版手机的保护套

package com.factoryPattern05;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:用于保护标配版手机的保护套
 */
public class HWStandardPhoneCase implements PhoneCase{
	
	public HWStandardPhoneCase() {
		System.out.println("工厂生产StandardPhoneCase用于保护标配版的华为手机");
	}

	@Override
	public void protectMobile() {
		System.out.println("HWStandardPhoneCase protectMobile()");
		
	}
	
}

保护高配版手机的保护套

package com.factoryPattern05;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:用于保护高配版手机的保护套
 */
public class HWProfessionalPhoneCase implements PhoneCase{

	public HWProfessionalPhoneCase() {
		System.out.println("工厂生产ProfessionalPhoneCase用于保护高配版的华为手机");
	}
	
	@Override
	public void protectMobile() {
		System.out.println("HWProfessionalPhoneCase protectMobile()");
	}
	
}


手机工厂接口

改进后的手机工厂不但需要生成手机还需要生产与之配套的保护套。

package com.factoryPattern05;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:手机工厂接口
 */
public interface MobileAbstractFactory {
	public abstract Mobile createMobile();
	public abstract PhoneCase createPhoneCase();
}

生产标配版手机及其保护套的工厂

package com.factoryPattern05;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:用于生产标配版手机及其保护套的工厂
 */
public class StandardMobileConcreteFactory implements MobileAbstractFactory {

	@Override
	public Mobile createMobile() {
		Mobile mobile=new HWStandardMobile();
		return mobile;
	}

	@Override
	public PhoneCase createPhoneCase() {
		PhoneCase phoneCase=new HWStandardPhoneCase();
		return phoneCase;
	}

}

生产高配版手机及其保护套的工厂

package com.factoryPattern05;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 * 示例描述:用于生产高配版手机及其保护套的工厂
 */
public class ProfessionalMobileConcreteFactory implements MobileAbstractFactory {
	
	@Override
	public Mobile createMobile() {
		Mobile mobile=new HWProfessionalMobile();
		return mobile;
	}

	@Override
	public PhoneCase createPhoneCase() {
		PhoneCase phoneCase=new HWProfessionalPhoneCase();
		return phoneCase;
	}

}

测试

package com.factoryPattern05;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {

	public static void main(String[] args) {
		Mobile mobile =null;
		PhoneCase phoneCase=null;
		MobileAbstractFactory mobileFactory=null;
		
		mobileFactory=new StandardMobileConcreteFactory();
		mobile=mobileFactory.createMobile();
		phoneCase=mobileFactory.createPhoneCase();
		
		mobileFactory=new ProfessionalMobileConcreteFactory();
		mobile=mobileFactory.createMobile();
		phoneCase=mobileFactory.createPhoneCase();
	}

}

在这里插入图片描述

嗯哼,我们来测试一下。要生产不同的手机及其与之匹配的保护套只需要建立不同的工厂就行啦;至于工厂怎么生产的细节问题就不用管了,而且工厂生产出来的手机和保护套必然是相互匹配的,不会出现错误的搭配!

小结

到了这里,我想不少人都有疑问了:这不和工厂方法模式基本完全一样么?无非是工厂里多了一个方法而已!嗯哼,从表面来看是这样的。那么,工厂方法模式和抽象工厂模式有什么区别么?关于这一点,不少书里都提到了产品,产品树,产品族,产品等级的概念。这些类似于八股文的东西看起来还是挺头疼的,理不清,斩不断!其实,不用过分纠结概念,我们只要理解其中的道理就行了。

嗯哼,在刚才这个抽象工厂模式的示例中我有意无意地在反复强调一点:手机要与手机保护套匹配,不能乱对应。比如:高配手机搭配一个标配的手机保护套是错误的。那么,我们怎么来杜类似的错误呢?我们让客户来选择么?这个不太现实,谁也不能保证客户不会犯错!既然这样,那么我们把手机和与之对应的保护套放到同一个工厂里;并建立起几个不同的工厂。于是,我们就可以让用户选"套餐"了,它选了什么套餐,我们就启用与之对应的工厂进行生成就行。比如,华为告诉穷士康:我要造100W部高配手机,并且每个手机配一个高档的保护套。穷士康接到这个订单之后把任务交给ProfessionalMobileConcreteFactory即可。从我们的代码也可以看出来:ProfessionalMobileConcreteFactory生成出来的比如是高配手机和高配的手机保护套,绝对不会乱套!

好了,说了这么多大白话,我们再来看抽象工厂模式的定义:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes

这句英文的主要含义是:抽象工厂模式为创建一组相关或相互依赖的对象提供一个接口!请问:什么叫做“相关或相互依赖的对象”?不就是我们这里的手机和与之对应的手机保护套么?!嗯哼,这么说应该就清楚多了!明白这点之后,我们趁热打铁,思考一下:如果抽象工厂模式里工厂里的方法只有一个的话,那么不就又变成了工厂方法模式了么? 所以说:抽象工厂模式和工厂方法模式的区别就在于需要创建的对象的复杂程度上。而且,在简单工厂模式,工厂方法模式,抽象工厂模式中这三者中抽象工厂模式是最为抽象、最具一般性的。

感悟

很多刚开始做开发的童鞋喜欢拿着一本厚厚的设计模式在角落里默默地啃。学习的劲头很足,态度也很端正,配得上10086个赞。在此,我也想提醒一下小伙伴们:学习态度和努力程度固然非常重要,但是我们也要注意学习方法。抛开实际应用和业务逻辑单纯地看设计模式是很难理解其精髓的。我们不妨将设计模式和自己的实际工作结合起来学习,从理论到实践,从书本到实战。

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谷哥的小弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值