工厂模式思想
属于设计模式中的创建型模式。
思想: 客户指定需要的对象,把需求告诉工厂,工厂负责创建对象并返回给客户。
简单工厂模式
定义: 由一个工厂来决定创建出哪一种类的实例。不属于23种设计模式
场景: 工厂类负责创建的对象较少。
优点: 客户端只需要传入一个正确的参数,就可以获取到对应的对象,无须知道创建对象的逻辑。
缺点: 工厂类职责相对过重,增加新的产品时需要修改工厂类的判断逻辑,违背了单一职责、开闭原则。
案例代码实现:
女娲造人的故事,女娲往锅炉放泥土来进行烧制泥人,可以烧制黑种人、黄种人、白种人。
抽象出人类,有白种人、黄种人。
public interface IHuman {
void getColor();
}
public class WhiteHuman implements IHuman {
@Override
public void getColor() {
System.out.println("white human ...");
}
}
public class YellowHuman implements IHuman {
@Override
public void getColor() {
System.out.println("yellow human ...");
}
}
创建人类的工厂,根据不同的类模板信息创建对应的人类对象。
public class HumanFactory {
public IHuman createHuman(Class<? extends IHuman> clazz) {
try {
if (null != clazz) {
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class NvWa {
public static void main(String[] args) {
HumanFactory humanFactory = new HumanFactory();
IHuman whiteHuman = humanFactory.createHuman(WhiteHuman.class);
whiteHuman.getColor();
IHuman yellowHuman = humanFactory.createHuman(YellowHuman.class);
yellowHuman.getColor();
}
}
工厂方法模式
定义: 每个产品体系都有对应的工厂叫工厂模式。
工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。
场景:
优点: 新增产品只需要增加对应产品的工厂,符合开闭原则,提高了系统的扩展性。
缺点: 类的个数容易过多。
案例代码实现:
有一个汽车代工厂,客户可向工厂提交订单,客户提供对应的底盘和引擎,工厂根据订单生产汽车交付给客户。底盘是一个产品体系,引擎是一个产品体系,底盘和引擎分别对应了不同的工厂。
底盘:
// 抽象出底盘接口
public interface IChassis {
void create();
}
// 奥迪汽车底盘
public class AudiChassis implements IChassis {
@Override
public void create() {
System.out.println("audi chassis ...");
}
}
// 起亚汽车底盘
public class KIAChassis implements IChassis {
@Override
public void create() {
System.out.println("KIA chassis ...");
}
}
引擎:
// 抽象出引擎接口
public interface IEngine {
void create();
}
// 奥迪发动机
public class AudiEngine implements IEngine {
@Override
public void create() {
System.out.println("audi engine ...");
}
}
// 起亚发动机
public class KIAEngine implements IEngine {
@Override
public void create() {
System.out.println("KIA engine ...");
}
}
底盘工厂:
public class ChassisFactory {
public static IChassis createChassis(Class<? extends IChassis> clazz) {
try {
if (null != clazz) {
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
引擎工厂:
public class EngineFactory {
public static IEngine createEngine(Class<? extends IEngine> clazz) {
try {
if (null != clazz) {
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
汽车工厂:
public class CarFactory {
public IChassis chassis;
public IEngine engine;
void createCar(Class<? extends IChassis> chassis, Class<? extends IEngine> engine) {
this.chassis = ChassisFactory.createChassis(chassis);
this.engine = EngineFactory.createEngine(engine);
}
}
客户:
public class Client {
public static void main(String[] args) {
CarFactory carFactory = new CarFactory();
carFactory.createCar(AudiChassis.class, AudiEngine.class);
carFactory.chassis.create();
carFactory.engine.create();
carFactory.createCar(KIAChassis.class, KIAEngine.class);
carFactory.chassis.create();
carFactory.engine.create();
}
}
运行结果:
可以看到,客户只需要传入需求,工厂就会根据客户的需求生产产品,客户无需知道具体的实现过程,这使用户与产品实现了一定程度的解耦(但产品与工厂耦合了)。
但问题也是显而易见的。例如用户传入一个Audi的底盘和一个KIA的引擎,工厂并不会理会,而会按照用户的选择,生产出一部古怪的汽车(或是这两个零件无法组合在一起)。在某些情况下,这种情况是不应该出现或者不允许的,怎么解决呢?这里引入抽象工厂模式。
抽象工厂模式
定义:
场景:
优点:
缺点:
产品族: 同一个品牌,比如:海尔、美的。
产品等级: 同一个产品,比如:冰箱、洗衣机。
抽象工厂模式与工厂方法模式的最大区别就在于:工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。
关于产品等级和产品族的概念,见下图:
对于工厂方法模式,每个工厂负责一个产品等级,各自生产零件,最后由总工厂组合成产品。而抽象工厂模式,则是一个总工厂负责统筹,总工厂下面的子工厂各自负责一个品族,生成产品,最后由是由总工厂将产品交付给客户。
案例代码实现:
底盘和引擎的实现和上面的工厂方法模式一样。
下面创建一个抽象工厂、Audi工厂和KIA工厂:
// 抽象一个工厂架构
public interface AbstractFactory {
Chassis createChassis();
Engine createEngine();
}
// 实现一个具体的工厂,这个工厂专门生产奥迪汽车
public class AudiFactory implements AbstractFactory {
@Override
public Chassis createChassis() {
return new AudiChassis();
}
@Override
public Engine createEngine() {
return new AudiEngine();
}
}
// 这个工厂专门生产起亚汽车
public class KIAFactory implements AbstractFactory{
@Override
public Chassis createChassis() {
return new KIAChassis();
}
@Override
public Engine createEngine() {
return new KIAEngine();
}
}
汽车工厂:
public class CarFactory {
private Chassis chassis;
private Engine engine;
// 这里直接传入对应产品族的工厂实例,杜绝了产品等级不一致的问题
public void createCar(AbstractFactory factory){
this.chassis = factory.createChassis();
this.engine = factory.createEngine();
System.out.println("A car is created.");
System.out.printf("CHASSIS: %s\n", chassis.identify());
System.out.printf("ENGINE: %s", engine.identify());
}
}
客户,生产一部起亚汽车:
public class Client {
// 下一个汽车订单
public void orderACar(AbstractFactory factory){
CarFactory cf = new CarFactory();
cf.createCar(factory);
}
}
// 测试类
public class ClientTest {
public static void main(String[] args) throws Exception{
Client client = new Client();
// 传入一个具体的工厂,就能生产出对应的汽车
client.orderACar(new KIAFactory());
}
}
可以看到,客户使用抽象工厂来创建需要的对象,客户只是面向产品的接口编程,根本无需知道工厂的具体实现。也就是说,客户端从具体的产品实现中解耦。而且,由于具体工厂限制了产品族对中每个零件的产品等级,从而解决了上面产品等级不对应的问题,不会再出现Audi底盘装载KIA引擎的车了。
另外,抽象工厂模式实现了产品与工厂的解耦,所以支持增加产品族。例如我们要增加一个BMW车的产品族,只需增加一个BMW工厂实例,和对应的零件实例就可以了,也就是说,我们可以通过实现AbstractFactory
、Chassis
和Engine
这几个接口就可以增加一个产品族。但如果想要增加产品等级,则需要修改工厂的具体实现。
总结
使用工厂模式就是提醒我们,写代码的时候,尽可能的避免使用new关键字创建对象,因为new关键字意味着依赖,如果一个类到处都是使用new关键字实例化,那么哪天这个类不用了,要换成其它的实现类,我们就需要到处修改,这样就太消耗时间了。
比如上面的工厂方法案例,现在已经研发出了第二代奥迪底盘,只需要创建一个第二代奥迪底盘的实现类,然后在底盘工厂替换下实现类,就可实现生产第二代底盘的奥迪汽车了。代码如下:
public class AudiChassis2 implements Chassis{
@Override
public String identify() {
return "this is a Chassis of Audi2.";
}
}
public class ChassisFactory {
// 生成底盘的方法,根据传入的类型生产对应类型的底盘
public static Chassis createChassis(String chassisType) {
Chassis chassis;
switch (chassisType) {
case "Audi": chassis = new AudiChassis2(); break;
case "KIA": chassis = new KIAChassis(); break;
default: chassis = null;
}
return chassis;
}
}
总之,工厂模式就是提醒我们,当需要实例化对象时,我们尽可能避免使用new去实例化,考虑为对象创建一个统一的来源。
产品族:同一个品牌,比如:海尔、美的。
产品等级:同一个产品,比如:冰箱、洗衣机。
具体的美的工厂。
具体的海尔工厂。
抽象工厂太复杂了,所以用的不多,最常用简单工厂和工厂方法。
简单工厂:一个工厂可以创建很多不同类型的产品。
工厂方法:一类产品(比如:冰箱)对应一个工厂。
抽象工厂:产品族(品牌)对应一个抽象工厂,然后一个具体的产品品牌(美的)实现抽象工厂。