工厂模式
-
简单工厂模式
案例:由厨师做菜为例子,厨师可以做肉和鱼,让顾客来进行点菜
public interface Chef { public void cook(); }
public class Meet implements Chef{ @Override public void cook() { System.out.println("做锅肉~"); } }
public class Fish implements Chef{ @Override public void cook() { System.out.println("做盘鱼~"); } }
没用到工厂模式下顾客点菜:
public class Customer { private int type; public static Chef getMenu(int type){ if(type == 1){ return new Meet(); }else if(type == 2){ return new Fish(); }else { return null; } } }
创建产品实例的方法在顾客类里面,这种方式虽然也能获取对应想要的菜,但是代码耦合性高,扩展性极其的差。试想一下,如果厨师学习到了新的菜品—蛋炒饭,那么我们就需要增加一个蛋炒饭类,并且在顾客代码中进行修改:
public class Egg implements Chef{ @Override public void cook() { System.out.println("我爱蛋炒饭~"); } }
public class Customer { private int type; public static Chef getMenu(int type){ if(type == 1){ return new Meet(); }else if(type == 2){ return new Fish(); }else if(type == 3){ return new Egg(); }else { return null; } } }
这就违反了开闭原则( 对扩展开放,对修改关闭),并且创建实例的方法可能会用到很多地方,修改量很大,比如现在把这个顾客类细分为男顾客、女顾客或者老人小孩等,这些类里面都用到了getMenu()方法,那么此时增加新菜品要修改量是不是就有点多了呢?结合实际生活也很好理解,想想自己去餐厅点菜时是直接去后厨和厨师点的吗?
所以需要用到简单工厂模式,本例中就相当于服务员:
public class Waiter { public Chef getMenu(int type){ if(type == 1){ return new Meet(); }else if(type == 2){ return new Fish(); }else if(type == 3){ return new Egg(); }else { return null; } } }
public class Customer { private Waiter waiter = new Waiter(); public Chef getMenu(int type){ return waiter.getMenu(type); } }
此时再有新菜品增加只需要去修改一个工厂类的代码就可以了,但是仍然是违反开闭原则,于是有了工厂方法模式。
-
工厂方法模式
工厂方法模式是把普通工厂就是把简单工厂中具体的工厂类,划分成两层:抽象工厂层+具体的工厂子类层。
public abstract class CookFactory { public abstract Chef createChef(); }
public class MeetChef extends CookFactory{ @Override public Chef createChef() { return new Meet(); } }
public class FishChef extends CookFactory{ @Override public Chef createChef() { return new Fish(); } }
相当于有一个烹饪工厂(抽象工厂),工厂里面有专门做肉的厨师(具体工厂),也有专门做鱼的厨师(具体工厂),而顾客只用告诉这个烹饪工厂自己要的是肉还是鱼,工厂就会返回对应菜品的厨师,然后厨师就开始做拿手菜。
public class Customer { public static Chef getMenu(CookFactory cookFactory){ return cookFactory.createChef(); } public static void main(String[] args) { Chef meet = Customer.getMenu(new MeetChef()); meet.cook(); Chef fish = Customer.getMenu(new FishChef()); fish.cook(); } }
工厂方法模式很好地解决了简单工厂模式违反开闭原则的问题,在这个模式下当有新的菜品增加进来时,只需要增加一个对应这个菜品的厨师即可,也就是具体工厂类:
public class EggChef extends CookFactory{ @Override public Chef createChef() { return new Egg(); } }
这不需要对任何之前已有的代码进行修改就能完成对新菜品的添加。
这个模式的优点也显而易见:
- 符合开闭原则:
- 新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
- 简单工厂模式需要修改工厂类的判断逻辑
- 符合单一职责原则:
- 每个具体工厂类只负责创建对应的产品
- 简单工厂中的工厂类存在复杂的if-else的逻辑判断,工厂方法模式对应的厨师直接做对应的菜,不需要传递type来判断做什么菜
同时也有美中不足:
- 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销
- 一个具体工厂只能创建一种具体产品
- 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类
应用场景:
- 当一个类不知道它所需要的对象的类时在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可。
- 当一个类希望通过其子类来指定创建对象时在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
- 符合开闭原则:
3.抽象工厂模式
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。每个具体的工厂负责一个产品族。抽象工厂的返回值为最高级抽象产品。
实质:就是将工厂方法模式中的具体工厂类也变成了抽象工厂。这样一个MeetChef类就可以不只是只能做肉了,还可以在需要时增加新的菜品,而且不改变原有的代码。
案例:举买车的例子。某客户想要购买一辆车,他要联系4S店,首先得有4S店(抽象工厂)的电话。客户上网查询(建造工厂),发现了宝马4S店(具体工厂)的电话和奔驰4S店(具体工厂)的电话。客户拨通了宝马4S店的电话(获取具体工厂),发现目前店里可以提供(生产)多款车型(具体产品)供客户选择(BMW 320、BMW 530,BMW 740)。客户拨通了奔驰4S店的电话(获取具体工厂),发现目前店里可以提供(生产)多款车型(具体产品)供客户选择(BenzC200、BenzE300)。
实现:
汽车类:
/**
* 最高级抽象产品,用于抽象工厂的建造方法的返回值
*/
public abstract class Car
{
abstract void drive();
}
宝马产品类:
/**
* 抽象产品
*/
public abstract class BMWCar extends Car
{
}
/**
* 具体产品BMW320
*/
public class BMW320 extends BMWCar
{
public void drive()
{
System.out.println("BMW320,运动酷炫。");
}
}
/**
* 具体产品BMW530
*/
public class BMW530 extends BMWCar
{
public void drive()
{
System.out.println("BMW530,时不我待。");
}
}
奔驰产品类:
/**
* 抽象产品
*/
public abstract class BenzCar extends Car
{
}
/**
* 具体产品C200
*/
public class BenzC200 extends BenzCar
{
public void drive()
{
System.out.println("BenzC200,实惠有面");
}
}
/**
* 具体产品E300
*/
public class BenzE300 extends BenzCar
{
public void drive()
{
System.out.println("BenzE300,商务气派");
}
}
工厂类:
/**
* 宝马工厂,覆盖所有宝马车型的构造方法
*/
public class BMWFactory extends AbstractFactory
{
public Car getCar(String type) throws ClassNotFoundException,
IllegalAccessException, InstantiationException
{
Class cl = Class.forName(type);
return (BMWCar)cl.newInstance();
}
}
/**
* 奔驰工厂,覆盖所有奔驰车型的构造方法
*/
public class BenzFactory extends AbstractFactory
{
public Car getCar(String type) throws ClassNotFoundException,
IllegalAccessException, InstantiationException
{
Class cl = Class.forName(type);
return (BenzCar)cl.newInstance();
}
}
抽象工厂类:
public abstract class AbstractFactory
{
public abstract Car getCar(String type);
}
超级工厂类:
/**
* 超级工厂类,建造工厂的工厂
*/
public class FactoryProducer
{
public static AbstractFactory getFactory(String type)
{
Class cl = Class.forName(type);
System.out.println("创建工厂"+type);
return (AbstractFactory)cl.newInstance();
}
}
验证:
/**
* 验证
*/
public class Demo
{
public static void main(String[] args) throws IllegalAccessException,
InstantiationException, ClassNotFoundException
{
AbstractFactory abstractFactory = FactoryProducer.getFactory("BMWFactory");
Car bmwCar = abstractFactory.getCar("BMW320");
bmwCar.drive();
Car bmwCar1 = abstractFactory.getCar("BMW530");
bmwCar1.drive();
Car bmwCar2 = abstractFactory.getCar("BMW740");
bmwCar2.drive();
AbstractFactory abstractFactory1 = FactoryProducer.getFactory("BenzFactory");
Car benzCar = abstractFactory1.getCar("BenzC200");
benzCar.drive();
Car benzCar1 = abstractFactory1.getCar("BenzE300");
benzCar1.drive();
}
}
优点:
- 符合开闭原则
- 克服了工厂方法模式下一个具体工厂只能创建一种具体产品的窘境,
- 对于使用工厂方法的类,如果要更换另外一种产品,不需要修改实例化的具体工厂类