全面了解工厂模式
- 工厂模式是啥?
- 简单工厂是工厂模式吗?
- 工厂模式的哼哈二将(工厂方法、抽象工厂)
一、工厂模式是啥?
我们知道在java中创建对象是需要使用new操作符,但有时候我们显示的进行实例化的时候就会经常造成初始化“耦合”的问题。
看下列代码有什么问题?
Fruit fruit;
if (apple){
//苹果
fruit = new Apple();
}else if (pear){
//梨
fruit = new Pear();
}else if (peach){
//桃
fruit = new Peach();
}
上面,代码我们可以看出 当我们创建具体水果类的时候,通常会像上面这种方式书写代码。究竟要实例化哪一个类,要在运行时根据条件决定。
当我们看到这样的代码,一旦有变化或者扩展,比如新增一个葡萄类就必须重新打开这段代码进行修改检查。通常这样修改过的代码将造成部分系统难维护和更新,而且容易犯错。
当我们新增或删除新的类时,需要直接在上述代码中修改,那么我们的代码并非“对修改关闭,对扩展开发”。
那我们该怎么办呢,根据OO设计原则,找出变化的方面,把它们从不变的部分中分离出来。
识别变的方面
假设我们有一个煎饼店,我们的代码可能会这么写:
//订单处理
JianBing orderJB(){
JianBing jb = new JianBing();
//准备
jb.prepare();
//摊煎饼
jb.bake();
//切
jb.cute();
//装袋
jb.bag();
return jb;
}
如果我们在做一些不同类型的煎饼,看代码这么写:
JianBing orderJB(String type){
JianBing jb = new JianBing();
//这段代码根据不同的类型,制作不同的煎饼,会根据不同地方人的口味会经常性的修改
//经常发生变化的部分
if(type.equals("huotui")){
//火腿
pancake = new HuoTuiJianBing();
}else if(type.equals("latiao")){
//辣条
pancake = new LatiaoJianBing();
}else if(type.equals("jidan")){
//鸡蛋
pancake = new JiDanJianBing();
}
//制作流程一般不会发生变化
jb.prepare();
jb.bake();
jb.cute();
jb.bag();
return jb;
}
我们可以看出上述代码并没有对修改关闭,如果煎饼店修改煎饼的不同风味的话,就必须对上面代码中的if语句进行修改,增加或删除对象,可以看出创建不同类型的煎饼的随着时间的推移和市场的变化,会经常的变化,代码也会被一改再改。
封装创建对象的代码
我们知道了下面代码是经常变化变化的部分:
//经常发生变化的部分
if(type.equals("huotui")){
//火腿
pancake = new HuoTuiJianBing();
}else if(type.equals("latiao")){
//辣条
pancake = new LatiaoJianBing();
}else if(type.equals("jidan")){
//鸡蛋
pancake = new JiDanJianBing();
}
我们就抽取,这段变化的代码,放到一个对象当中,让一个对象专门负责创建煎饼实例,如果任何对象想要创建煎饼那么就找这个对象就行了。我们称这个新对象为"工厂"。
二、简单工厂
我们定义一个类,为煎饼封装创建对象的代码。代码如下:
public class SimpleJianBingFactory {
//只负责创建具体实例
JianBing createJB(String type){
JianBing jb = null;
if(type.equals("huotui")){
pancake = new HuoTuiJianBing();
}else if(type.equals("latiao")){
pancake = new LatiaoJianBing();
}else if(type.equals("jidan")){
pancake = new JiDanJianBing();
}
return jb;
}
}
SimpleJianBingFactory是我们新创的类,他只负责一件事,就是帮助创建不同的具体实例对象。
现在我们可以用工厂来为我们创建煎饼,我们煎饼店要做的改变是:
public class JianBingStore {
SimplePizzaFactory factory;
//我们必须要先有一个 工厂给我们创建煎饼实例
public JianBingStore(SimplePizzaFactory factory) {
this.factory = factory;
}
JianBing orderJB(String type){
//这里我们把new对象 变成了让工厂类返回一个对象,这里不再进行实例化了
JianBing jb = factory.createPizza(type);
jb.prepare();
jb.bake();
jb.cute();
jb.bag();
return jb;
}
}
SimpleJianBingFactory就是我们经常说的简单工,简单工厂其实并不是一种设计模式,反而更像是一种编码习惯。有些开发人员经常把这个编程习惯误认为"工厂模式"。
小贴士:
设计模式中,所谓的"实现一个接口" 并 "不一定" 表示 "写一个类利用implement来实现某个java接口"。
"实现一个接口" 泛指 "实现某个超类型(可以是类或接口)的某个方法"。
三、工厂模式哼哈二将:工厂方法、抽象工厂
(一)工厂方法
现在我们想多开几家煎饼店,我们要做北京和上海各开一家店,因为每个地方的人的口味不同,所以,开的连锁店也做的产品也有些不同。
首先,看看JianBingStore要做的改变:
public abstract class JianBingStore {
//订单处理的流程不变
JianBing orderJB(String type){
JianBing jb = createJB(type);
jb.prepare();
jb.bake();
jb.cute();
jb.bag();
return jb;
}
//我们把工厂创建的对象的方式,改成了一个抽象发方法来创建对象
abstract JianBing createJB(String type);
}
现在已经有一个JianBingStore作为超类;让每个其他地方的点继承该超类,每个子类自行决定做什么类型的煎饼。
现在来创建煎饼店:
//上海店
public class JianBingSHStore extends JianBingStore {
@Override
JianBing createPizza(String type) {
//这里创建的具体实例,都是上海风味的
if (type.equals("huotui")){//火腿
return new SHHuotuiJianBing();
}else if (type.equals("jidan")){//鸡蛋
return new SHJidanJianBing();
}else if (type.equals("jiliu")){//鸡柳
return new SHJiliuJianBing();
}
return null;
}
}
//广州店
public class JianBingGZStore extends JianBingStore {
@Override
JianBing createPizza(String type) {
//这里创建的具体实例,都是深圳风味的
if (type.equals("latiao")){//辣条
return new GZLatiaoJianBing();
}else if (type.equals("jidan")){//鸡蛋
return new GZJidanJianBing();
}else if (type.equals("jiliu")){//鸡柳
return new GZJiliuJianBing();
}
return null;
}
}
请注意,超类的orderJB并不知道真正创建的煎饼是哪一种,他只知道这个煎饼可以被准备、烤、切、装袋。
原本是有一个对象负责所有具体的实例化,现在通过对JianBingStore做的一些改变,变成了让一群子类来负责实例化。而超类中的抽象方法createPizza(String type) 就相当于一个工厂一样,用来产生具体的实例,那么这个抽象方法就是叫工厂方法。
工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。
//1. 工厂方法是抽象的,依赖子类来创建对象
//2. 工厂方法必须返回一个产品。超类中定义的方法,通常使用到工厂方法的返回值
//3. 工厂方法将客户(也就是超类中的代码,例如orderJB())和时间创建具体产品的代码进行分隔出来
//4. 工厂方法可以需要参数也可能不需要 来指定产品
abstract Product factoryMethod(String type)
工厂模式的定义:
定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
注:工厂方法和超类是否总是抽象的?
不,可以定义一个默认的工厂方法来产生具体的产品,这么一来,即使创建者(超类)没有任何子类也可以创建产品。
(二)抽象工厂
为了保证在各地开的分店的原材料能够保质,我们可以创建一个工厂,专门负责生产原材料 供给各个店铺。因为不同地方的店需要的原材料不完全一样,我们不能在北京创建一家工厂来生成原材料,然后在送往上海、广州等地,这样很不划算。
现在,我们先建造一个家工厂生成原材料;这个工厂将负责创建所有店需要的原材料。
我们先定义一个接口,这个接口负责创建所有原材料:
/**
* 原材料工厂 负责创建所有的原材料
*/
public interface JianBingIngredientFactory {
//面团
public Dough createDough();
//酱料
public Sauce createSauce();
//蔬菜
public Veggies[] createVeggies();
//肉类
public Meat[] createMeat();
}
建好原料工厂后,为每个区域的店建造一个工厂。我们需要创建一个JianBingIngredientFactory子类来实现每一个创建方法。根据不通过的地方,我们具体实现的细节是不一样的。
//上海原料厂 供上海
public class SHJianBingIngredientFactory implements JianBingIngredientFactory {
@Override
public Dough createDough() {
return new SHDough();
}
@Override
public Sauce createSauce() {
return new SHSauce();
}
@Override
public Veggies[] createVeggies() {
return new Veggies[] = {new Garlic(),new Onion()};
}
@Override
public Meat[] createMeat() {
return new Meat[] = {new JLW(),new WZW()};
}
}
//广州原材料厂 类似
我们重新开始做煎饼,使用工厂原料。先从抽象的JianBing类开始:
public abstract class JianBing {
String name;
String dough;//面团
String sauce;//酱料
Veggies veggies[];//蔬菜
Meat meat[];//肉类
/*
准备 准备各种原材料
*/
abstract void prepare();
/**
* 烘烤
*/
void bake(){
System.out.printf("烘烤2分钟");
}
/**
* 切片
*/
void cut(){
System.out.printf("切片");
}
/**
* 装袋
*/
void bag(){
System.out.printf("装袋");
}
public String getName() {
return name;
}
}
创建一个上海煎饼类:
//创建一个 上海的火腿煎饼
public class SHHuotuiJianBing extends JianBing {
SHJianBingIngredientFactory factory;
public SHHuotuiJianBing(SHJianBingIngredientFactory factory) {
this.factory = factory;
}
@Override
void prepare() {
//从上海原料工厂 获取原料
dough = factory.createDough();
sauce = factory.createSauce();
meat = factory.createMeat();
}
}
上海煎饼店也发送了些许改变:
//上海店
public class JianBingSHStore extends JianBingStore {
@Override
JianBing createJB(String type) {
//上海原材料工厂
JianBingIngredientFactory factory = new SHJianBingIngredientFactory();
//这里创建的具体实例,都是上海风味的
if (type.equals("huotui")){//火腿
//接收一个原料工厂
return new SHHuotuiJianBing(factory);
}else if (type.equals("jidan")){//鸡蛋
return new SHJidanJianBing(factory);
}else if (type.equals("jiliu")){//鸡柳
return new SHJiliuJianBing(factory);
}
return null;
}
}
这样当我们下单时,就会执行以下流程:
-
首先我们创建一个上海煎饼店 JianBingStore store = new SHJinBingStore()
-
store.orderJB("huotui") 接收订单
-
store.orderJB("huotui")会调用createPizza()方法
-
当createPizza()被调用时,也就开始涉及原料工厂了
-
一但调用了prepare()方法,那么工厂就要准备好原料了
-
最后被制作出来了打包
抽象工厂的定义:
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道和关心实际产出的产品是什么。
抽象工厂的方法经常以工厂方法的方法来实现,抽象工厂的任务是定义一个负责创建一组产品的接口。这个接口内的每个方法都负责创建一个具体的产品。
总结:
所有的工厂模式都是用来封装对象的创建的。
工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建过程进行封装的目的。
抽象工厂是定义一个负责创建一组产品的接口,该接口内的每一个方法都是负责创建一个产品。
工厂方法特点:
工厂方法可以吧客户代码从需要实例化的具体类中解耦。或者如果目前还不知道将来要实例化那些具体类时,也可以使用,使用方法很简单,只要把该类继承并实现里面的工厂方法就可以了。
抽象工厂特点:
当需要创建一组产品可以使用抽象工厂。利用抽象工厂 需要扩展一个类,因为抽象工厂需要定义一个接口