抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。可以理解成是多个工厂方法的组合。
在工厂方法模式中,具体创建者每次使用都只能创建一个同类型的对象,假如现在需要的是多个不同类型的对象,工厂方法就满足不了需求了。这时我们可以把多个工厂方法组合到一个类,这就是抽象工厂模式,它就是专门用来创建多个产品,也可以说是创建产品家族的。
Creator抽象工厂:定义了一系列的产品生产行为,客户端直接引用,由未实现的工厂方法组成,子类必须实现其工厂方法创建产品家族。
ConcreteCreator具体工厂:实现抽象工厂接口,负责实现工厂方法,一个具体工厂可以创建一组产品。
Product抽象产品:产品族的父类,由此可以衍生很多子产品。
ProductA1/A2/B1/B2具体产品:实现了相应的产品接口,衍生自抽象产品,由工厂方法直接创建。
首先搞清楚产品等级和产品族的概念,比如:手机有小米手机、华为手机,它们都是手机,这些具体的手机和抽象手机就构成了一个产品等级结构。同样的,路由器有小米路由器,华为路由器,这些具体的路由器和抽象路由器就构成了另外一个产品等级结构,实质上产品等级结构即产品的继承结构。小米手机位于手机产品等级结构中,小米路由器位于路由器的产品等级结构中,而小米手机和小米路由器都是小米公司生产的,就构成了一个产品族,同理,华为手机和华为路由器也构成了一个产品族 。划重点就是产品族中的产品都是由同一个工厂生产的,位于不同的产品等级结构。
抽象工厂模式的两种情况:①符合开闭原则;②不符合开闭原则(又称抽象工厂模式“开闭原则”的倾斜性)
优点
具体产品隔离,无需关系创建细节。
将一个系列的产品统一到一起创建。
增加新的产品族很方便,无须修改已有系统,符合开闭原则。
缺点
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则。
增加了系统的抽象性和理解难度。
一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节。
系统中有多于一个的产品族,但每次只使用其中某一产品族。
属于同一个产品族的产品将在一起使用, 这一约束必须在系统的设计中体现出来。
产品等级结构稳定, 设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。
(1)简单工厂模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。不符合开闭原则,中心是一个实体工厂类,工厂负责创建的对象比较少,客户端只知道传入工厂的参数,不需要关注对象创建的细节。简单来说:只有一个实体工厂类;一个实体工厂类生产所有的产品类,工厂根据传入的参数判断具体生产哪个产品给客户。
(2)工厂方法模式:定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。只有一个抽象产品类;一个实体工厂类生产一种产品类,客户需要知道生产该产品的工厂类名称。
(3)抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。有多个抽象产品类;一个实体工厂类可以生产多种产品类,客户可以从一个工厂获得所有想要的产品。
将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。
建造者模式主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
建造者(Builder):为创建一个产品对象的各个部件指定抽象接口。
具体建造者(ConcreteBuilder):实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并 提供一个检索产品的接口。
指挥者(Director):指挥并构造一个使用Builder接口的对象,负责和客户(Client)对话。
产品(Product):表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
优点
用户只需要指定要建造的类型就可以得到它们,而具体的建造过程和细节不需要知道。
建造代码与表示相分离,如果要改变一个产品的内部表示,只要再定义一个新的具体的建造者就可以了。
建造过程由指挥者来控制,建造细节由一个抽象类来控制,对于实现建造细节的具体类来说,不会遗漏某一个步骤。
缺点
产品必须有共同点,范围有限制。
如内部变化复杂,会有很多的建造类。
建造者模式适用于所创建的产品具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,如做奶茶和做烧饼。
以开奶茶店为例,奶茶的原料可以由多个厂提供,使用抽象工厂来统一供应原料。假如配制奶茶只需要奶类和茶类,这些配件是稳定的,配制过程也是稳定的,但是搭配方式是多样的,比如牛奶红茶,牛奶绿茶等。
(1)首先创建工厂,也就是原材料总工厂,相当于一个大型市场,所有的实体工厂都开在这里。
public interface Factory {
public Milk CreateMilk();
public Tea CreateTea();
}
(2)原材料工厂中涉及很多原材料产品种类,现在创建这些产品种类,即创建抽象产品。
public interface Milk {
}
public interface Tea {
}
(3)接着创建所需的具体产品。
public class PureMilk implements Milk {
PureMilk(){
System.out.print("加纯牛奶");
}
}
public class Yogurt implements Milk {
public Yogurt(){
System.out.print("加酸奶");
}
}
public class GreenTea implements Tea {
public GreenTea(){
System.out.print("加绿茶");
}
}
public class RedTea implements Tea {
public RedTea() {
System.out.print("加红茶");
}
}
(4)具体原材料准备好了,则分到具体的工厂制作。
public class AFactory implements Factory {
//A厂
public Milk CreateMilk(){
Milk milk = new PureMilk();
System.out.println("(产地:A厂)");
return milk;
}
public Tea CreateTea(){
Tea tea = new GreenTea();
System.out.println("(产地:A厂)");
return tea;
}
}
public class BFactory implements Factory {
//B厂
public Milk CreateMilk(){
Milk milk = new Yogurt();
System.out.println("(产地:B厂)");
return milk;
}
public Tea CreateTea(){
Tea tea = new RedTea();
System.out.println("(产地:B厂)");
return tea;
}
}
以上为抽象工厂模式的步骤。
下面开始建造者模式。
(5)有了原材料工厂,可以着手制作奶茶了。店长(奶茶建造者Builder)为抽象类,可以理解为店长为做奶茶了制作了一个“食谱”(里面包含制作需要的原料及方法)其余事情店长不需要做。
public abstract class MilkTeaBuilder {
public String name;
public Milk milk;
public Tea tea;
public abstract void buildMilk();
public abstract void buildTea();
public abstract void create();
}
(6)由后厨们(具体建造者ConcreteBuilder)按照建造者Builder的“食谱”制作具体的奶茶,原料由市场里的工厂提供。
public class GreenMilkConBuilder extends MilkTeaBuilder{
@Override
public void buildMilk() {
Factory factory;
factory=new AFactory();
milk=factory.CreateMilk();
}
@Override
public void buildTea() {
Factory factory;
factory=new AFactory();
tea=factory.CreateTea();
}
@Override
public void create() {
System.out.print("正在准备牛奶绿茶中......");
}
}
public class GreenYogurtConBuilder extends MilkTeaBuilder{
@Override
public void buildMilk() {
Factory factoryB;
factoryB=new BFactory();
milk=factoryB.CreateMilk();
}
@Override
public void buildTea() {
Factory factoryA;
factoryA=new AFactory();
tea=factoryA.CreateTea();
}
@Override
public void create() {
System.out.print("正在开始准备酸奶绿茶中......");
}
}
public class RedMilkConBuilder extends MilkTeaBuilder{
Factory factoryA;
Factory factoryB;
public RedMilkConBuilder() {
factoryA=new AFactory();
factoryB=new BFactory();
}
@Override
public void buildMilk() {
milk=factoryA.CreateMilk();
}
@Override
public void buildTea() {
tea=factoryB.CreateTea();
}
@Override
public void create() {
System.out.println("正在准备牛奶红茶中......");
}
}
public class RedYogurtConBuilder extends MilkTeaBuilder{
@Override
public void buildMilk() {
Factory factoryB;
factoryB=new BFactory();
milk=factoryB.CreateMilk();
}
@Override
public void buildTea() {
Factory factoryB;
factoryB=new AFactory();
tea=factoryB.CreateTea();
}
@Override
public void create() {
System.out.print("正在开始准备酸奶红茶中......");
}
}
(7)奶茶一切准备就绪,可以开店了。店里有服务员(指挥者类Director)一方面它隔离了客户与生产过程,即客户只看到菜单只能选择商品不能看到生产过程;另一方面它负责控制产品的生成过程。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象,简单来说服务员Director和店长Builder说顾客Client要红茶,然后店长Builder安排具体后厨ConcreteBuilder生产相应产品。
import java.util.Scanner;
public class MilkTeashop {
public void menu() {
System.out.println("************MENU************");
System.out.println("1.RedMilk 2.GreenMilk");
System.out.println("3.RedYogurt 4.GreenYogurt");
System.out.println("*************END*************");
}
public void making(MilkTeaBuilder milkTea) {
milkTea.create();
milkTea.buildMilk();
milkTea.buildTea();
}
public MilkTeaBuilder selectMilkTea() {
MilkTeaBuilder milkTea=null;
Scanner s =new Scanner(System.in);
System.out.print("请选择你的饮料");
int num=s.nextInt();
switch (num) {
case 1:
milkTea=new RedMilkConBuilder();
break;
case 2:
milkTea=new GreenMilkConBuilder();
break;
case 3:
milkTea=new RedYogurtConBuilder();
break;
case 4:
milkTea=new GreenYogurtConBuilder();
break;
default:
System.out.println("输入错误!");
break;
}
making(milkTea);
return milkTea;
}
}
(8)测试一下奶茶店的运行情况
public class DrinkClient {
public static void main(String[] args) {
MilkTeashop coco = new MilkTeashop();
coco.menu();
coco.selectMilkTea();
}
}
结果
由于水平有限,文章中难免有错误的地方,欢迎指出错误或不足之处,共同进步 Thanks♪(・ω・)ノ