工厂模式
工厂模式:属于创建型设计模式,目的就是实现解耦,使客户端并只需要知道目标生成对象的名字即可,并不需要知道具体的创建过程,创建的任务交由工厂实现。
简单工厂模式
说到工厂模式,那还是得提一嘴简单工厂模式的,这是一种简化的工厂模式,只有一个工厂类,可扩展性差,如果要新增一个对象,则同时要对这个工厂类进行修改,这是违背了开闭原则的。
下面来个例子,看看简单工厂模式的实现,假设有一家罐头水果的工厂,里面能够生产各种罐头水果,例如用各种苹果生产的罐头苹果:进口苹果生产的罐头,本地苹果生产的罐头
先来个产品抽象类
/**
* 苹果罐头
*/
public interface ICannedApple {
public void showApple();
}
再来各种苹果做出罐头苹果的具体实现类
/**
* 进口 苹果
*/
public class ImportedApple implements ICannedApple{
@Override
public void showApple() {
System.out.println("进口苹果做的苹果罐头");
}
}
/**
* 本地 苹果
*/
public class LocalApple implements ICannedApple{
@Override
public void showApple() {
System.out.println("本地苹果做的苹果罐头");
}
}
再来一个罐头水果的生产工厂
/**
* 水果罐头工厂
*/
public class CannedAppleFactory {
public static final int IMPORTED_APPLE = 0;
public static final int LOCAL_APPLE = 1;
public static ICannedApple createCannedApple(int type){
ICannedApple cannedApple = null;
if(IMPORTED_APPLE == type){
cannedApple = new ImportedApple();
}else if(LOCAL_APPLE == type){
cannedApple = new LocalApple();
}
return cannedApple;
}
}
当然水果罐头的生产工厂也可以用反射的方式优雅一点的实现
/**
* 水果罐头生产工厂
*/
public class CannedAppleFactory {
public static <T extends ICannedApple>T createCannedApple(Class<T> c){
ICannedApple cannedApple = null;
try {
cannedApple = (T)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T)cannedApple;
}
}
想要生产哪种类型的苹果罐头就通知工厂类,让工厂类来生产:
public class Client {
public static void main(String[] args) {
CannedAppleFactory cannedAppleFactory = new CannedAppleFactory();
ICannedApple importedApple = cannedAppleFactory.createCannedApple(CannedAppleFactory.IMPORTED_APPLE);
ICannedApple localApple = cannedAppleFactory.createCannedApple(CannedAppleFactory.LOCAL_APPLE);
//以下两行对应的是反射方式实现的工厂类
// ICannedApple importedApple = CannedAppleFactory.createCannedApple(ImportedApple.class);
// ICannedApple localApple = CannedAppleFactory.createCannedApple(LocalApple.class);
importedApple.showApple();
localApple.showApple();
}
}
运行结果:
如果这时,要在这个罐头水果工厂要新增一种苹果来进行制作,那么我们就要新增一个具体的苹果罐头实现类,还要对现有的水果罐头工厂进行修改。这明显是违背了开闭原则。
工厂方法模式
为了避免,每次增加水果类型都要去修改水果罐头工厂的内容,那么我们是不是可以换种生产思路呢?例如:按照苹果类型进行划分,每种水果就在一个工厂进行生产,进口苹果工厂生产进口苹果罐头,本地苹果生产本地苹果罐头。这就是工厂模式,拥有一组实现相同接口的工厂类,每个工厂类对应一个对象。
代码搞上:
苹果还是那苹果,现在把原来的工厂分割开来,每个专门生产一种水果罐头
/**
* 罐头苹果工厂
*/
public interface ICannedFactory {
ICannedApple createCannedApple();
}
/**
* 用进口苹果生产苹果罐头的工厂
*/
public class ImportedAppleFactory implements ICannedFactory{
@Override
public ICannedApple createCannedApple() {
ICannedApple cannedApple = new ImportedApple();
return cannedApple;
}
}
/**
* 用本地苹果生产苹果罐头的工厂
*/
public class LocalAppleFactory implements ICannedFactory{
@Override
public ICannedApple createCannedApple() {
ICannedApple cannedApple = new LocalApple();
return cannedApple;
}
}
在实际的生产中,就可以通过通知不同的工厂来生产不同的苹果罐头了:
public class Client {
public static void main(String[] args) {
ICannedFactory importedAppleFactory = new ImportedAppleFactory();
ICannedFactory localAppleFactory = new LocalAppleFactory();
ICannedApple importedApple = importedAppleFactory.createCannedApple();
ICannedApple localApple = localAppleFactory.createCannedApple();
importedApple.showApple();
localApple.showApple();
}
}
运行结果如下:
这就是工厂方法模式,每次增加一种新产品,并不需要对原来的代码进行更改而是新增新的代码,做到了符合对修改封闭对扩展开发的开闭原则。
抽象工厂模式
虽然工厂模式很好解决了违背开闭原则的问题,但是也存在隐患。那就是如果要生产的对象比较多的时候,就会有比较多的工厂类,这样就会让项目复杂化,不好维护。所以我们要提高一点点,把工厂做一些优化。
现在我们不仅仅要生产苹果罐头了,还要生产芒果和草莓罐头,芒果和草莓也有进口和本地之分。。。。但是我们不能够无休止的新增工厂吧,所以需要把工厂整合一下
为了简便,尽量减少代码展示抽象模式,这里我们就当只增加芒果这种水果吧。分析一下目前要生产的水果罐头种类:进口苹果罐头,本地苹果罐头,进口芒果罐头还有本地芒果罐头。既然苹果和芒果都有进口和本地之分,那么我们是不是可以分成2个工厂,一个做进口水果的,一个做本地水果的。
开搞:
结合前面的代码,先把芒果罐头搞出来
/**
* 芒果罐头
*/
public interface ICannedMango {
public void showMango();
}
/**
* 进口 芒果
*/
public class ImportedMango implements ICannedMango{
@Override
public void showMango() {
System.out.println("进口芒果做的芒果罐头");
}
}
/**
* 本地 芒果
*/
public class LocalMango implements ICannedMango{
@Override
public void showMango() {
System.out.println("本地芒果做的芒果罐头");
}
}
好了,现在需要生产的东西都准备好,是时候建立工厂了:
/**
* 罐头工厂应该有的业务
*/
public interface ICannedFactory {
/** 生产苹果罐头 */
ICannedApple createCannedApple();
/** 生产芒果罐头 */
ICannedMango createCannedMango();
}
/**
* 进口水果罐头工厂
*/
public class ImportedFactory implements ICannedFactory{
@Override
public ICannedApple createCannedApple() {
/** 进口水果罐头工厂 生产出来的苹果罐头应该是进口的苹果罐头 */
ICannedApple cannedApple = new ImportedApple();
return cannedApple;
}
@Override
public ICannedMango createCannedMango() {
/** 进口水果罐头工厂 生产出来的芒果罐头应该是进口的芒果罐头 */
ICannedMango cannedMango = new ImportedMango();
return cannedMango;
}
}
/**
* 本地水果罐头工厂
*/
public class LocalFactory implements ICannedFactory{
@Override
public ICannedApple createCannedApple() {
/** 本地水果罐头工厂 生产出来的苹果罐头应该是本地的苹果罐头 */
ICannedApple cannedApple = new LocalApple();
return cannedApple;
}
@Override
public ICannedMango createCannedMango() {
/** 本地水果罐头工厂 生产出来的芒果罐头应该是本地的芒果罐头 */
ICannedMango cannedMango = new LocalMango();
return cannedMango;
}
}
工厂建好后,看看是如何生产出各种水果罐头的:
public class Client {
public static void main(String[] args) {
ICannedFactory importedFactory = new ImportedFactory();
ICannedFactory localFactory = new LocalFactory();
ICannedApple importedApple = importedFactory.createCannedApple();
ICannedMango importedMango = importedFactory.createCannedMango();
ICannedApple localApple = localFactory.createCannedApple();
ICannedMango localMongo = localFactory.createCannedMango();
importedApple.showApple();
importedMango.showMango();
localApple.showApple();
localMongo.showMango();
}
}
运行结果:
像这种把产品分组,组内不同的产品对应的是同一个工厂的不同方法的设计模式就是抽象工厂模式了。抽象工厂模式是违背了开闭原则的,当需要增加新产品时,需要修改工厂类和工厂接口。但是只需要进行方法的添加,避免了修改原有的方法。
总结一下
- 简单工厂模式
简单工厂模式有唯一的一个工厂类,工厂类是通过传入的参数,用条件判断进行创建对象(可以是if-else
,也可以是when
) - 工厂方法模式
工厂方法模式是由多个工厂类创建不同的产品,规避了条件判断同时实现了开闭原则 - 抽象工厂模式
抽象工厂模式是把产品进行分组,同组的产品由同一个工厂类的不同方法创建,减少了工厂类的数量,但是对开闭原则做出了让步。