有款游戏叫模拟人生,我这里就例子就叫北漂人生吧。
北漂人生 规定安家模式
a.结婚 b.买房 c. 买车 d. 生娃
先看几种情况:
1. 我初来帝都,按照北漂人生规则,如果要安家,要遵守
a. 结婚
b. 买车(不可以,纳税不够5年)
c. 买房(不可以,纳税不够5年)
d. 生娃(不享受北京相关政策福利)
2. 我来帝都5年,要安家,也遵守
a. 结婚
b. 买房(有资格,看你储备和运气)
c. 买车(同上)
d. 生娃(不享受)
3. 帝都郊区人民
a. 结婚
b. 买房
c. 买车
d. 生娃(可以2个)
注:以上规则不适用“我爸是李刚”等开挂模式。
作为一个全方位的考虑,你要实现很多种情况,但是规则比较固定,所以假如我有个工厂类,并且用if/else来解决
- if(//初来帝都,不够5年){
- //执行了安家模式abcd,只有结婚无限制,其他有特殊规则
- }else
- if(//来帝都5年){
- //安家模式abcd,放开部分限制
- }else
- if(//帝都郊区){
- //安家模式abcd,遵守规则
- }else
- if(//帝都土著){
- //安家模式abcd,福利分房,补贴
- }
太乱!时也许你会整理整理,于是对外地人和本地人做了个区分
- if(//外地人){
- //5年限制
- }else
- if(//本地人){
- }
然后你开始面对一个问题,每个if/else里都有abcd,但是又略有不同,如何重构?
一般我们考虑继承或者组合两种方式,设计模式可以考虑策略,装饰器等。那这里就是讲工厂方法,所以就采用继承。
- if(//外地人){
- if(//level A){
- //类unlocalA的对象
- }
- else if(//level B){
- //类unlocalB的对象
- }
- }
- else if(//本地人){
- if(//level A){
- //类localA的对象
- }else if(//level B){
- //类localB的对象
- }
- }
再次重构呢?能不能更好一点,假如我新增加一个老外模式进来,能不能不修改原来的工厂类了呢?
尝试将工厂分为本地工厂和外地工厂,这样再加入新的老外工厂就不会影响其他已经封装的源代码了。
记得在区别 工厂方法 和 抽象工厂的这个图,是这样的:
假如我添加老外安家:
通过上面的推导过程,我们无赖似的推出了个工厂方法模式。再补充一下,这个例子并不是很适合工厂方法,我们来看看工厂方法适合的场景。
工厂方法模式适合的情况
我们注意到几个问题
1. 变化少:产品(我们这里的是产品是安家)接口内容不会有太多变化。
2. 松耦合:新增加一工厂以及相关产品,不需要改动原来的代码
3. 相同接口:产品都是实现同一个接口
4. 相同层级:每个工厂,层级都基本相同。
如果我们仔细回忆一下,spring在对ORM框架和EJB对于安全策略的集成,都与工厂方法有非常相似的地方。 都是制定了一些共同的约束,非常符合我们变化少的原则。
何时用?
工厂方法模式一般在一些比较大的系统中会用到,尤其在做spring这种组件分离,但遵循某些共同约束的场景下,结合一些其他模式,将会更加灵活强大。
我为这个模式写个些粗糙的代码:
层级:
- /**
- * 家庭级别
- * @author 书生
- *
- */
- public enum Level {
- /*高级*/A,
- /*普通*/B
- }
抽象工厂
- /**
- * 创建家庭
- * @author 书生
- *
- */
- public abstract class FamilyBuild {
- public Family buildFamily(Level level) {
- Family family;
- family = marry(level);
- family.buyHouse();
- family.buyCar();
- family.babyBirth();
- return family;
- }
- /**
- * 结婚组成家庭,传入级别
- * @param level
- * @return
- */
- protected abstract Family marry(Level level);
- }
一个具体的工厂
- public class UnLocalFamilyBuild extends FamilyBuild {
- @Override
- protected Family marry(Level level) {
- Family family;
- if(level==Level.A) {
- family = new UnLocalSeniorFamily();
- }else {
- family = new UnLocalCommonFamily();
- }
- return family;
- }
- }
家庭接口
- /**
- * 家庭
- * @author 书生
- *
- */
- public abstract class Family {
- /*户主等相关信息描述*/
- public String hostName;
- /*家庭住址*/
- public String adr;
- public void buyHouse() {
- System.out.println("buy house");
- }
- public void marry() {
- System.out.println("Important moment,Happy Times");
- }
- public void babyBirth() {
- System.out.println("make new people :)");
- }
- public void buyCar() {
- System.out.println("buy a car");
- }
- }
普通外地人接口
- /**
- * 普通外地人组件家庭
- * @author 书生
- *
- */
- public class UnlocalCommonFamily extends Family {
- public UnlocalCommonFamily() {
- hostName = "工作不满5年的外地人";
- adr = "租房";
- }
- @Override
- public void babyBirth() {
- //TODO 生孩子相关政策
- }
- @Override
- public void buyHouse() {
- // TODO 不足5年,不能买房
- //完全重写
- }
- @Override
- public void buyCar() {
- // TODO 不足5年,不能买车
- // 完全重写
- }
- }
如果我想创建一个普通外地人家庭,需要
- public class TestFactoryMethod {
- public static void main(String[] args) {
- FamilyBuild fb = new UnLocalFamilyBuild();
- Family family = fb.buildFamily(Level.B);
- }
- }
我想你也注意到了,new UnLocalFamilyBuild(); 每一个工厂都有一套自己的规则。如果能做到只要一个配置就得到需要的工厂功能,而不需要知道具体工厂类会不会更好?答案是也许会。我们在spring中也看到有多个Factory的情况,当一个工厂类,包含的信息非常丰富,或者通过类名,能够基本知道他所代表的喊一声时,这已经是一个不错的工厂了。这个可能会引发一个争论,到底精简到什么地步是最好,或者说,设计模式的原则都是合理的吗? 不过这不是我们要讨论的,我们通过此文最重要的是对工厂方法的思想理解。
我上传一下我写的工厂方法的一些代码,大多是伪码。