设计模式——工厂模式(Factory)

有一个问题很困惑:我们在进行一些初始化操作的时候,需要去new对象,但是new的使用不正是针对实现编程吗?违背了针对 接口编程的原则。

针对接口编程可以隔绝掉后来系统的发生的改变,如果代码针对接口编写,我们可以通过多态,组合,使新类实现该接口,但是代码使用大量具体类的时候,一旦加入一个新的具体类,就必须修改原来的代码,也违背了对修改关闭的原则。这时候我们从基本入手“找出会变化的部分,把他们从不变的部分分离出来”。

引用《HeadFirst》中的经典场景。我们现在拥有一个披萨店,我们需要设计一套系统。

一 寻找变化与相同点

1.我们建一个pizza类,类里有pizza制作的过程。并实现pizza商店,里面可以制作pizza(new一个实例对象)。

public class Pizza {
	public void prepare() {}
	public void bake() {}
	public void cut() {}
	public void box() {}
}
public class PizzaStore {
	Pizza pizza=new Pizza();
	
	public Pizza orderPizza(){
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
		
	}

}

2.我们会有多种pizza,所有的特色Pizza都继承PIzza类。增加一些代码,根据不同的选择,我们能做出多种多样的pizza。

public class PizzaStore {
	Pizza pizza=new Pizza();	
	public Pizza orderPizza(String type){
		if (type.equals("cheese")) {
			pizza=new CheesePizza();
		} else if(type.equals("greek")){
			pizza=new GreekPizza();
		}
		
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;		
	}
}

二 定义一个简单工厂

上面的代码当写出来那一刻,就发现了一个问题,如果这样写,每次增加/删除一个新种类就要修改一次商店的代码。“找出会变化的部分,把他们从不变的部分分离出来” ,开始封装。

我们把披萨对象创建的代码单独拉入一个类,姑且就当一个简单的披萨工厂。

public class SimplePizzaFactory {	
	public Pizza createPizza(String type){
		Pizza pizza = null;
		if (type.equals("cheese")) {
			pizza=new CheesePizza();
		} else if(type.equals("greek")){
			pizza=new GreekPizza();
		}
		return pizza;
	}
}

我们的SimplePizzaFactoy类只用来帮客户创建披萨,所有的客户都通过createPizza方法实例化新对象。

我们的PIzzaStore同时也发生了改变。

public class PizzaStore {
	SimplePizzaFactory simplePizzaFactory = new SimplePizzaFactory();

	public PizzaStore(SimplePizzaFactory simplePizzaFactory) {
		//构造器引入一个工厂
		this.simplePizzaFactory = simplePizzaFactory;
	}

	public Pizza orderPizza(String type) {
		Pizza pizza;
		//通过传入类型来创建特殊披萨
		pizza = simplePizzaFactory.createPizza(type);
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}

}
我们把new操作符替换成工厂对象的创建方法,不在使用具体的实例化。

用我们的类图来梳理下。


恩,基本搞定了有个简单工厂。但是,但是,其实简单工厂并不是一个设计模式,而是一种编程习惯。

三 真正的工厂模式

如果我们的披萨店设了分店,一个简单的Factory显然满足不了需求,所以我们就会有很多商店,初步构想是子商店类都继承自 父级商店。

假设我们有家纽约的披萨加工厂,我们会出现新的方式制作披萨。

	NYPizzaFactory nyPizzaFactory=new NYPizzaFactory();
	PizzaStore nyStore=new PizzaStore(nyPizzaFactory);
	nyStore.orderPizza("Veggie");

但是我们在子工厂继承SimpleFactory生产的时候,新的商店的制造披萨的方法(creatPizza)会发生差异,子类工厂需要修改creatPizza(),这样就导致创建PIzza的过程无法控制。这时候我们的工厂类就显得比较多余了。我们需要建造一个框架,将新的披萨商店对象与创建披萨的过程捆绑在一起,但是又保持一定的弹性,做到部分的质量控制。

我们可以把工厂类概括为一个方法。

public abstract class PizzaStore {
	SimplePizzaFactory simplePizzaFactory = new SimplePizzaFactory();

//	public PizzaStore(SimplePizzaFactory simplePizzaFactory) {
//		//构造器引入一个工厂
//		this.simplePizzaFactory = simplePizzaFactory;
//	}

	public Pizza orderPizza(String type) {
		Pizza pizza;
		//通过传入类型来创建特殊披萨
		pizza = simplePizzaFactory.createPizza(type);
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
	
	//实例化Pizza的方法从一个类转为一个方法(工厂方法引入)
	protected abstract Pizza createPizza();
}

新的工厂方法用来处理对象的创建,并且因为抽象方法的原因,子类的继承需要处理此方法,也就是说工厂方法封装到了子类中。所以程序中父类(PizzaStore)的代码就和子类(NYPizzaStore)的创建对象代码解耦了。



1.我们的PIzzaStore也需要修改了。

public abstract class PizzaStore {

	public Pizza orderPizza(String type) {
		Pizza pizza;
		//通过传入类型来创建特殊披萨
		pizza = createPizza(type);
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
	
	//子类自定义实现创建细节
	protected abstract Pizza createPizza(String type);

}

2.然后创建NY子类商店创建的独特NYPizza

public class NYPizzaStore extends PizzaStore{
	@Override
	protected Pizza createPizza(String type) {
		Pizza pizza = null;
		//独特的NY类型Pizza
		if (type.equals("NYcheese")) {
			pizza=new NYCheesePizza();
		} else if(type.equals("NYgreek")){
			pizza=new NYGreekPizza();
		}
		return pizza;
	}
}

3.修改Pizza类。给披萨加上三种属性。名称,面团类型,酱料类型,一套佐料。为了更好展示效果,代码写的丰满一点。

public abstract class Pizza {
	// Pizza基本属性
	String name;
	String dough;// 面团类型
	String sauce;// 酱汁类型
	ArrayList<String> toppingsArrayList = new ArrayList<String>();

	void prepare() {
		System.out.println(name + "准备中...");
		System.out.println(dough + "加入...");
		System.out.println(sauce + "加入...");
		for (String toppingName : toppingsArrayList) {
			System.out.println(toppingName);
		}
		System.out.println("佐料加入...");
	}

	public void bake() {
		System.out.println("烘烤中...");
	}

	public void cut() {
		System.out.println("切块中...");
	}

	public void box() {
		System.out.println("打包中...");
	}

	public String getName() {
		return name;
	}
}

4.定义独特的NYPizza。继承Pizza类。

public class NYCheesePizza extends Pizza {

	public NYCheesePizza() {
		name = "NYCheesePizza";
		dough = "NYDough";
		sauce = "NYSauce";
		toppingsArrayList.add("NYTopping");
	}

	public void cut() {// 可以覆盖父类方法,形成自己独特的风格
		System.out.println("NY独特的切割技巧...");
	}

}

OK,大功告成,测试代码,上~


工厂模式的使用为了new对象的封装,是为了让子类决定具体怎么创建对象。

代码敲完,暂时抛开该死的教材。画个通用类图重新整理下思路。


我们通过oderProduct方法和工厂抽象方法链接起来,同事也把生产知识(类型匹配)封装到了子类,形成了一个最简单的工厂框架。

搬下官方定义。工厂方法模式定义了一个创建对象的接口,但由子类决定具体的实例化对象。工厂方法让类把实例化推迟到子类。

真正的工厂模式与我们上面的简单工厂的区别在于:

1.简单工厂把全部的事情,在一个地方处理完了,然后工厂方法是创建一个框架,让子类完成创建。

2.工厂方法中,orderPizza()方法提供了一般的框架,并且以抽象的工厂方法创建具体类。可以利用继承,让子类决定创建的实例类型。简单工厂仅仅是把创建对象代码拉出来(封装)。但是因为简单工厂不能变更正在创建的产品,所以不具有弹性,落了下乘。

看下第一部分我们不适用任何工厂的代码,类中代码有具体类的实例化,也就是说依赖某种具体类。代码中应该尽量减少对具体类的依赖,恩,这个在OO设计原则中有一条专门阐述了。依赖倒置原则(Dependency Inversion Principle),要依赖抽象,不要依赖具体类跟针对接口编程,不针对实现编程也很像。不能让商店依赖产品,不能让创建者依赖创建对象,不能让高层依赖底层,俩者都依赖抽象最好了。

遵循下面几个原则可能会做的更好。(书上说的)

1.变量不可以持有具体类的的引用(new)

2。不要让类派生自具体类(选择接口和抽象类)

3.不要覆盖基类已经实现的方法。(如果覆盖已经实现的方法,那么基类就不是一个真正被适合继承的对象。基类中实现的方法,所有子类应该共享

PS:表示抽象工厂后来再补吧,暂时我也有点晕。

猜你喜欢

转载自blog.csdn.net/ma598214297/article/details/80714840