简单工厂
概念:在Java代码中,我们不希望把对象的创建和逻辑代码混在一起,而是希望把对象的创建封装在一个接口中,调用这个接口(方法)就可以返回一个被实例化的对象。这样的好处是使得代码逻辑清晰,功能模块分离,增加代码的可读性和可修改性,比如这个方法:
Pizza pizza = nyStore.createPizza("cheese");
public Pizza createPizza(String type) {
Pizza pizza;
if(type.equals("cheese")){
pizza=new CheesePizza();
}else if(type.equals("veggie")){
pizza=new VeggiePizza();
}
return pizza;
}
代码分析:这里简单地模拟一个比萨店生成比萨的过程。当所有new比萨对象的代码都被封装在了一个create方法中时,想要生产某种pizza只要把相应的名字传入就可以了,pizza这个引用就会被实例化为不同的具体的pizza。
简单工厂的特点:将客户程序从具体类解耦。
优缺点分析:好处是实现了把对象创建的代码和客户代码分离,把实例化的部分全部封装了起来。缺点是这样仅仅是修改了代码的组织形式,代码的灵活性很差,一旦“菜单”改变,就意味着要修改代码,也就是说在不同的店里,都需要不同的create方法。所以,这只是一个简单的工厂,只做了一件事,把对象的创建封装,并不是真正意义上的工厂模式。
工厂方法
概念:如何让比萨店适应变化,就是我们接下来要做的事情。不同的地方有不同的比萨店,他们需要实现不同的工厂方法,创建不同的比萨,但也有一些相同的地方,比如生产比萨的切块,加热等流程。我们需要把store变成一个抽象类,所有store子类都有的代码可以在store中实现,其余的代码(比如工厂方法)定义为抽象方法,交给子类去实现。
public abstract class PizzaStore {
/**
* 点披萨的方法
* @param type
* @return
*/
public Pizza orderPizza(String type){
Pizza pizza;
pizza=creatPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza creatPizza(String type);//这个方法在不同的地区交给不同的工厂
}
特点:把对象的创建托付给了子类,由子类实现具体的工厂方法并创建对象。
优缺点分析:这样的好处就是子类可以定义自己的创建比萨的方法,相同部分的代码又能对各加盟店的制作工艺有统一的要求,父类不需要关心工厂方法的实现,也不需要关心具体对象的创建。缺点是这依然只能生产一种产品,因为工厂是被具体实现的,这部分代码是“硬代码”。
抽象工厂
概念:抽象工厂是工厂方法的一种改进,它实现了通过对工厂的改变使得生产多种比萨成为了可能。依然是根据设计模式中抽象出需要变化的部分的这条原则而设计的,我们把工厂设计为接口,比如不同地区的工厂可以单独实现这个接口,同时,不同的工厂也可以使用不同的原材料,比如cheese,dough等,它们也被抽象出来设计为接口。抽象工厂的设计原则用到了依赖的倒置,具体的类依赖于抽象,所有变化的部分都被设计为了抽象的代码,这样就可以使得代码拥有最大限度的灵活性。
设计中用到的几组接口:比萨店及其子类、比萨及其子类、工厂及其子类、各种原料及其子类。下面一一展示代码实现:
一、比萨店及其子类
①比萨店抽象类:
public abstract class PizzaStore {
/**
* 点披萨的方法
* @param type
* @return
*/
public Pizza orderPizza(String type){
Pizza pizza;
pizza=creatPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza creatPizza(String type);//这个方法在不同的地区交给不同的工厂
}
②纽约比萨店:
public class NYPizzaStore extends PizzaStore{
Pizza creatPizza(String type) {
Pizza pizza=null;
PizzaIngredientFactory pizzaIngredientFactory=new NYPizzaIngredientFactory();
if(type.equals("cheese")){
pizza=new CheesePizza(pizzaIngredientFactory);
pizza.setName("NY Cheese Pizza");
}else if(type.equals("veggie")){
pizza=new VeggiePizza(pizzaIngredientFactory);
pizza.setName("NY Cheese Pizza");
}
return pizza;
}
}
③芝加哥比萨店:
public class ChicagoPizzaStore extends PizzaStore{
Pizza creatPizza(String type) {
Pizza pizza=null;
PizzaIngredientFactory pizzaIngredientFactory=new CHPizzaIngredientFactory();
if(type.equals("cheese")){
pizza=new CheesePizza(pizzaIngredientFactory);
pizza.setName("CH Cheese Pizza");
}else if(type.equals("veggie")){
pizza=new VeggiePizza(pizzaIngredientFactory);
pizza.setName("CH Cheese Pizza");
}
return pizza;
}
}
二、比萨及其子类
①比萨抽象类:
public abstract class Pizza {
String name;//名字
Dough dough;//面团类型
Sause sauce;//酱料类型
ArrayList toppings=new ArrayList(); //一套佐料
/**
* 准备的方法
*/
abstract void prepare();
void bake(){
System.out.println("Bake for 25 minutes at 350");
}
void cut(){
System.out.println("Cutting the Pizza");
}
void box(){
System.out.println("Place pizza in box");
}
public String getName(){
return this.name;
}
public void setName(String name){
this.name=name;
}
}
②、奶酪比萨:
public class CheesePizza extends Pizza{
PizzaIngredientFactory pizzaIngredientFactory;
public CheesePizza(PizzaIngredientFactory pizzaIngredientFactory){
this.pizzaIngredientFactory=pizzaIngredientFactory;
}
void prepare(){
System.out.println("Preparing "+name);
dough=pizzaIngredientFactory.createDough();
sauce=pizzaIngredientFactory.createSause();
}
}
③、蔬菜比萨:
public class VeggiePizza extends Pizza {
PizzaIngredientFactory pizzaIngredientFactory;
public VeggiePizza(PizzaIngredientFactory pizzaIngredientFactory){
this.pizzaIngredientFactory=pizzaIngredientFactory;
}
void prepare(){
System.out.println("Preparing"+name);
dough=pizzaIngredientFactory.createDough();
sauce=pizzaIngredientFactory.createSause();
}
}
三、工厂及其子类
①、工厂抽象类:
public interface PizzaIngredientFactory {
public Dough createDough();
public Sause createSause();
public Cheese createCheese();
public Clams createClam();
}
②、纽约工厂:
public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
@Override
public Dough createDough() {
return new ThinCrustDough();
}
@Override
public Sause createSause() {
return new MarinaraSouse();
}
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
@Override
public Clams createClam() {
return new FreshClams();
}
}
③、芝加哥工厂:
public class CHPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThinCrustDough();
}
@Override
public Sause createSause() {
return new MarinaraSouse();
}
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
@Override
public Clams createClam() {
return new FreshClams();
}
}
四、各种原料及其子类
例如面团及其子类:
public interface Dough {
}
public class ThickCrustDough implements Dough{
}
public class ThinCrustDough implements Dough{
}
其他原料就不一一列出。
五、测试类
这里点两份奶酪比萨,一份来自纽约比萨店,另一方来自芝加哥比萨店,它们分别会用当地的工厂生产原料来制作比萨。
测试代码:
public class PizzaTestDrive {
public static void main(String[] args){
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chStore = new ChicagoPizzaStore();
Pizza pizza;
pizza = nyStore.orderPizza("cheese");
System.out.println("orderd a "+pizza.getName()+"\n");
pizza = chStore.orderPizza("cheese");
System.out.println("orderd a "+pizza.getName()+"\n");
}
}
生产比萨的流程逻辑:
1、调用order方法,会先执行其中的create方法,在create方法中先new一个工厂实例。
2、然后根据需要制作的产品名称确定实例化哪一种pizza,实例化pizza并把工厂对象传入它的构造方法。
3、调用prepare方法,向传入的工厂对象中获取原料。
4、再依次执行bake,cut,box方法,生产出比萨并返回。
运行结果:
抽象工厂的优点:根据依赖倒置原则设计,所有具体的实现类都依赖于抽象接口;对象的创建封装,用户代码和对象的创建得到解耦;代码灵活可变,工厂方法即方法内涉及到的所有内容都可以根据实际需要灵活变化。
工厂模式涉及到的OO原则
1、多用组合,少用继承:抽象工厂把产品创建的过程涉及到的类设计为接口,可以根据实际需要修改实现类,灵活多变。
2、针对接口编程,不针对实现编程:从简单工厂到抽象工厂,我们可以看到具体的实现逐渐被抽象,最终硬代码变为了可以改变的软代码。
3、为交互之间的松耦合而不断努力:把客户端代码和服务端代码解耦,实现功能的扩展不影响客户端的代码功能。
4、类对扩展开放,对修改关闭:避免修改客户端的源代码,只是通过调整抽象出来的接口不断扩展功能。
5、依赖抽象,而不依赖具体类:依赖倒置,通过依赖抽象实现松耦合设计,实现丰富的功能扩展。