版本声明:转载请注明出处。未经允许,禁止商业用途。
定义
定义了一个创建对象的接口,但是由子类决定要实例化的类是哪一个。工厂方法把类的实例化推迟到子类中。
实现方法
基类Product定义工厂方法所创建的对象的接口,派生类ConcreteProduct实现Product中的接口。
基类Creator定义一个创建Product的抽象工厂方法,派生类ConcreteCreator实现这个抽象工厂方法,由ConcreteCreator决定要实例化哪种ConcreteProduct。
需要说明的是工厂方法可以不是抽象的,可以让工厂方法默认产生某种ConcreteProduct。工厂方法也不是必须参数化。如果工厂只产生一种对象,那么就不需要参数化。
效果
工厂模式帮助我们将Product的实现和使用解耦。如果增加ConcreteProduct或者改变ConcreteProduct的实现,Product和Creator并不会受影响,更重要的是使用Product的客户不受影响。只需要修改对应的ConcreteCreator。
实例
很多程序员,当然还有非程序员都喜欢玩即时战略游戏。为了提高大家的兴趣,我为大家举一个例子。实现一个即时战略游戏中生产不同种族不同类型的士兵的组件。考虑篇幅,每个种族我都只实现了一种类型的士兵。感兴趣的话,读者可以自行添加其它类型的士兵。
角色对应关系:
Soldier是Product。HumanInfantry(人族步兵)和OrcInfantry(兽族步兵)是ConcreteProduct。
SoldierTrainingGroud是Creator。HumanSoldierTrainingGroud和OrcSoldierTrainingGroud是ConcreteCreator。
SoldierTest模拟客户,创建和使用具体类型的Soldier。
Java代码如下
Soldier.java:
package soldierfactory;
/* 这个类不依赖于任何具体类。
* 这里体现了面向对象设计原则中的依赖倒置原则,
* 依赖抽象,但是不依赖具体类。
* 具体类依赖于抽象类Soldier,因为它们实现了Soldier的接口,
* 这就是依赖倒置。*/
public abstract class Soldier {
String name;
/* 还可以定义攻击力、防御力、射程、移动速度、满血值、消耗黄金数量、消耗木材数量等 */
String getName() {
return name;
}
void provideResource() {
System.out.println("providing gold and wood");
}
void train() {
System.out.println("training");
}
void equip() {
System.out.println("equipping");
}
}
SoldierTrainingGroud.java
package soldierfactory;
/* 因为类里有抽象方法,所以类是抽象类。
* 与Soldier类似,这里也体现了依赖倒置原则。
* requireSoldier()定义了一个框架。
* 明确了具体类可以做哪些事情,
* 但是具体每件事情如何做,具体类可以自己决定。
* 另外,requireSoldier()并不知道哪个子类会最终做这些事情。 */
public abstract class SoldierTrainingGroud {
/* 工厂方法createSoldier()使类的实例化推迟到子类中。
* 工厂方法是abstract的,所以子类必须实现它,实现类的实例化。
* 工厂方法返回一个Product
* */
abstract Soldier createSoldier(String type);
public Soldier requireSoldier(String type) {
/* 将变化的部分,即实例化具体类的代码从应用中抽离,使它不会干扰应用的其它部分。
* 客户使用接口createSoldier(),
* 即针对接口编程,而不是针对实现编程。
* 针对接口编程,可以隔离变化,
* 通过多态,可以使用任何实现了该接口的具体类。
* 如果直接使用具体类,一旦加入新的具体类,就需要修改代码,
* 这使得requireSoldier()无法对修改关闭 */
Soldier soldier = createSoldier(type);
System.out.println("producting a "+ soldier.name);
soldier.provideResource();
soldier.train();
soldier.equip();
return soldier;
}
}
HumanInfantry.java:
package soldierfactory;
public class HumanInfantry extends Soldier{
HumanInfantry(){
name = "Human Infantry";
/* 还可以对攻击力、防御力、射程、移动速度、满血值、消耗黄金数量、消耗木材数量等进行赋值 */
}
/* 按需覆盖基类Soldier中的方法 */
void provideResource() {
/* 制造不同的兵种,消耗的黄金和木材数量不同 */
System.out.println("providing gold and wood for Human Infantry");
}
void train() {
/* 训练不同的兵种,所需的时间不同 */
System.out.println("training Human Infantry");
}
void equip() {
/* 装备不同的兵种,所需的时间不同 */
System.out.println("equipping Human Infantry");
}
}
HumanSoldierTrainingGroud.java:
package soldierfactory;
public class HumanSoldierTrainingGroud extends SoldierTrainingGroud{
/* 实现SoldierTrainingGroud中的抽象方法createSoldier()
* 不同种族的士兵训练场可以生产不同类型的士兵 */
Soldier createSoldier(String type) {
/* 生产步兵 */
if(type.equals("infantry")) {
return new HumanInfantry();
} else return null;
/* 这里也可以添加cavalry(骑兵)、archer(弓箭手)*/
}
}
OrcInfantry.java:
package soldierfactory;
public class OrcInfantry extends Soldier{
OrcInfantry(){
name = "Orc Infantry";
}
void provideResource() {
System.out.println("providing gold and wood for Orc Infantry");
}
void train() {
System.out.println("training Orc Infantry");
}
void equip() {
System.out.println("equipping Orc Infantry");
}
}
OrcSoldierTrainingGroud.java:
package soldierfactory;
public class OrcSoldierTrainingGroud extends SoldierTrainingGroud{
Soldier createSoldier(String type) {
if (type.equals("infantry")) {
return new OrcInfantry();
} else return null;
}
}
SoldierTest.java:
package soldierfactory;
public class SoldierTest {
public static void main(String[] args) {
SoldierTrainingGroud humanSoldierTrainingGroud = new HumanSoldierTrainingGroud();
SoldierTrainingGroud orcSoldierTrainingGroud = new OrcSoldierTrainingGroud();
/* 生产一个人族步兵。
* 客户使用Product类(Soldier类),不直接使用ConcreteProduct类(HumanInfantry类),
* 即针对接口编程,而不是针对实现编程。
* 通过多态,实际可以使用任何实现了该接口的具体类。 */
Soldier soldier1 = humanSoldierTrainingGroud.requireSoldier("infantry");
System.out.println("producted a "+soldier1.getName()+"\n");
/* 啊啊啊,吼吼,我要去战斗! */
/* 生产一个兽族步兵 */
Soldier soldier2 = orcSoldierTrainingGroud.requireSoldier("infantry");
System.out.println("producted a "+soldier2.getName()+"\n");
}
}
运行结果:
producting a Human Infantry
providing gold and wood for Human Infantry
training Human Infantry
equipping Human Infantry
producted a Human Infantry
producting a Orc Infantry
providing gold and wood for Orc Infantry
training Orc Infantry
equipping Orc Infantry
producted a Orc Infantry
谢谢阅读。欢迎提问。