工厂方法设计模式
工厂方法的目的是为了让创建对象的过程从客户端(使用该对象的代码)解耦出来。也就是说,工厂方法解决的问题是如何优雅地创建对象。当你的程序可能出现以下情况时,就应该考虑使用工厂方法。
- 将要创建的对象拥有一个共同的父类
- 这个对象的类型是在运行时动态决定的
工厂方法设计模式这样定义:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
这句话什么意思?定义了创建对象的接口
这一句可能会有点迷惑。其实这个接口并非一定就是要使用java的interface
声明的类,它可以是一个类,接口只是父类的泛指。简单来说,就是父类通过暴露一个抽象方法,子类按照自己的方式去实现这个方法
即可。考虑一个问题:在一个可以更换武器的游戏之中,应该怎样设计才能优雅地创建这些武器对象。假设武器有:木剑,石剑,AK47, 霰弹枪。
粗暴的实现方法
假设我们把这些武器类定义为WoodSword
、RockSword
、AK47
、ShotGun
,然后我们定义了一个Character
类来使用这些武器。Character内部定义了一个attack
方法,这个attack方法单纯调用了武器的attack
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public static void main(String[] args){ Character character = new Character(); Scanner scanner = new Scanner(System.in); Weapon weapon = null; if(scanner.hasNextLine()){ String weaponType = scanner.nextLine(); switch (weaponType) { case "woodsword": weapon = new WoodSword(); break; case "rocksword": weapon = new RockSword(); break; case "ak47": weapon = new AK47(); break; case "shotgun": weapon = new ShotGun(); break; } } if(weapon != null) { character.setWeapon(weapon); character.attack(); } } |
然而这个方法问题很明显,它首先违反了面向对象编程中对修改关闭的原则
,其次,这样的代码根本就没有办法复用,所以,这是一个很糟糕的实现。
使用工厂方法改造
这些武器类中,可以看出只有枪和剑,现在我们通过简单的抽象把上面糟糕的代码进行重构。先说说重构的思路:定义一个WeaponFactory
父类,该类定义了一个抽象方法createWeapon
,用于返回一个武器装置,可以通过继承这个父类创建不同的武器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public static void main(String[] args){ Character character = new Character(); Scanner scanner = new Scanner(System.in); String type = scanner.nextLine(); Weapon weapon; //创建一个造剑工厂 WeaponFactory factory = new SwordWeaponFactory(); //使用工厂方法创建武器 weapon = factory.createWeapon(type); if(weapon != null) { character.setWeapon(weapon); character.attack(); } } |
代码清爽了不少,更重要的是,这样的代码更加灵活,复用性也更高。接着我们来看一看SwordWeaponFactory
的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class SwordWeaponFactory extends WeaponFactory { @Override public Weapon createWeapon(String weaponType) { Weapon weapon = null; switch (weaponType) { case "woodsword": weapon = new WoodSword(); break; case "rocksword": weapon = new RockSword(); break; } return weapon; } } |
现在再反过来看一看一开头定义的这句话:工厂方法让类把实例化推迟到子类
。现在,我们应该明白这句话的意思了吧,也许我们应该说,真正决定实例化哪一个类的应该是游戏的玩家。
就是这么简单,没错,这就是工厂方法。使用工厂方法只需要三个步骤:
- 定义一个父类,并开放一个方法让子类实现。
- 创建该父类的子类。
- 客户端通过该子类创建对象
抽象工厂设计模式
抽象工厂模式和工厂方法有相似的地方,首先它们都是用来创建对象的设计模式,其次,它们都是通过父类开放公共方法的方式来实现。不同的是,抽象工厂模式创建的是一系列相关的对象,而工厂方法只是创建一个对象,这也是它们主要的不同。
抽象工厂模式的定义:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
很官方的定义,咋一看似乎并不那么直观。但是很明确的一点是,它用来创建对象的家族,即多个对象,这多个对象共同组成一个产品
。
抽象工厂举例
一个简单的例子,每一台电脑都是由多个产品组成的,例如cpu,内存,主板,电源,机箱。那么,我们就可以把这些东西抽象到一个接口之中,子类只要实现这个接口,就能生产出一台可运作的电脑,但父类可以不管它们的具体实现。
抽象工厂的UML关系图如下:
父类只提供创建电脑的接口,具体如何去生产完全由子类自己去决定:
用什么品牌的内存、主板、电源、机箱、CPU。现在先看看项目中用到的类:
解说一下执行流程:顾客来到电脑商店说:我要订购一台dell的电脑,然后,店主就拿来一台电脑。至于电脑的装配过程,顾客不需要知道,这些东西都封装到了ComputerStore中
。代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class Entry { public static void main(String[] args){ ComputerStore store = new ComputerStore(); Computer asusComputer = store.orderComputer(Computer.ASUS); asusComputer.run(); Computer dellComputer = store.orderComputer(Computer.DELL); dellComputer.run(); Computer hpComputer = store.orderComputer(Computer.HP); hpComputer.run(); } } |
ComputerStore中的代码是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class ComputerStore { ComputerFactory factory; public Computer orderComputer(int type){ switch(type){ case 1: factory = new AsusFactory(); Computer asusComputer = new AsusComputer(); asusComputer.box = factory.createBox(Box.HANGJIA); asusComputer.cpu = factory.createCpu(CPU.AMD); asusComputer.mainboard = factory.createMainborad(Mainboard.WEIXING); asusComputer.memory = factory.createMem(Memory.KONSTON); asusComputer.power = factory.createPower(Power.JINHETIAN); return asusComputer; case 2: factory = new DellFactory(); Computer dellComputer = new DellComputer(); dellComputer.box = factory.createBox(Box.HANGJIA); dellComputer.cpu = factory.createCpu(CPU.INTELL); dellComputer.memory = factory.createMem(Memory.WEIGANG); dellComputer.power = factory.createPower(Power.JINHETIAN); dellComputer.mainboard = factory.createMainborad(Mainboard.WEIXING); return dellComputer; case 3: factory = new HPFactory(); Computer hpComputer = new HPComputer(); hpComputer.box = factory.createBox(Box.XIANMA); hpComputer.cpu = factory.createCpu(CPU.AMD); hpComputer.memory = factory.createMem(Memory.WEIGANG); hpComputer.power = factory.createPower(Power.HANGJIA); hpComputer.mainboard = factory.createMainborad(Mainboard.HUASHUO); return hpComputer; default: System.out.println("没有这种品牌的电脑。。。"); return null; } } } |
可以看到,里面的ComputerFactory
就是一个抽象工厂,这个类封装了如何安装电脑的规则。工厂代码如下:
1 2 3 4 5 6 7 |
public interface ComputerFactory { CPU createCpu(int type); Box createBox(int type); Memory createMem(int type); Mainboard createMainborad(int type); Power createPower(int type); } |
就拿华硕电脑工厂来举例,华硕工厂实现了ComputerFactory
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class AsusFactory implements ComputerFactory { @Override public CPU createCpu(int type) { CPU cpu = new CPU(); cpu.setPartsType(type); return cpu; } @Override public Box createBox(int type) { Box box = new Box(); box.setPartsType(type); return box; } @Override public Memory createMem(int type) { Memory mem = new Memory(); mem.setPartsType(type); return mem; } @Override public Mainboard createMainborad(int type) { Mainboard mainboard = new Mainboard(); mainboard.setPartsType(type); return mainboard; } @Override public Power createPower(int type) { Power power = new Power(); power.setPartsType(type); return power; } } |
代码里面的每一个组件,比如CPU都可以使用一个工厂方法来封装,这里为了简单起见,只使用了一个setPartsType方法来模拟
来到这里,基本就可以解释什么是抽象工厂了。每一个电脑组件就是相互依赖的对象,它们共同组成一台电脑。
电脑的组装规规则都定义在父类ComputerFactory中。
抽象工厂的问题:如果以后接口更新,例如添加其他部件,那么子类也要一并更改。毕竟,子类的实现依赖于父类。
总结
工厂方法和抽象工厂的相同点:
- 它们都是用来创建对象,目的是为了是创建对象的过程从客户端解耦出来,实现了松耦合。
- 对象的类型是运行时动态决定的
不同点:
工厂方法创建的是一个对象。
抽象工厂创建的是多个对象,且这些对象之间具有一定的依赖关系。或者说它们共同构成另一个完整的对象。
完整代码下载:https://github.com/whathegeek/abstractdesignpattern
转载请注明:Pure nonsense » 工厂方法和抽象工厂设计模式