在正式的介绍工厂模式和抽象工厂模式之前,我们来先简单的认识下简单工厂,简单工厂其实并不是一个设计模式,反而更像是一种约定俗称的编程习惯。下面就以一个糖葫芦的例子来分析一下吧:
这是一个抽象的糖葫芦类。因为在糖葫芦的制作过程中,穿串、熬制甜料、浇糖过程都是不变的,所以我们创建了一个抽象类,不过这依然符合针对接口编程,不针对实现编程的OO原则,因为这里的接口的含义为广义的超类型。
public abstract class Product {
/**
* 选取原料,包括山楂、橘子、糯米等
*/
abstract void prepare();
/**
* 穿串
*/
public void chuanChuan() {
System.out.println("将选取的原料穿串");
}
/**
* 熬制调料
*/
public void createSweetener(){
System.out.println("熬制白糖调料");
}
/**
* 浇在串上冷却
*/
public void castSweetenerToChuan() {
System.out.println("将调料汁浇在串上并冷却");
}
}
这是一个具体的山楂糖葫芦。
public class ShanZhaProduct extends Product{
@Override
void prepare() {
System.out.println("选取精品山楂");
}
}
这是一个具体的橘子糖葫芦。
public class OrangeProduct extends Product {
@Override
void prepare() {
System.out.println("选取精品橘子");
}
}
这是一个具体的糯米糖葫芦。
public class RiceProduct extends Product {
@Override
void prepare() {
System.out.println("选取精品糯米,并揉成饭团");
}
}
这是一个简单工厂类。
public class SimpleFactory {
// 可根据客户的不同口味需求,提供不同的糖葫芦
public Product createProduct(int type) {
switch (type) {
case SimpleFactoryTest.SHANZHA:
// 山楂材料
return new ShanZhaProduct();
case SimpleFactoryTest.ORANGE:
// 橘子材料
return new OrangeProduct();
case SimpleFactoryTest.RICE:
// 糯米材料
return new RiceProduct();
default:
return null;
}
}
}
我们可以看出简单工厂将对象的创建细节封装起来(符合封装变化的OO原则),使对象的具体实例化过程从ProductStore代码中删除,让ProductStore从与具体的糖葫芦的依赖中解脱。
这是客户购买糖葫芦的店铺。
public class ProductStore {
//持有简单工厂对象
SimpleFactory factory;
public ProductStore(SimpleFactory factory) {
this.factory = factory;
}
//购买糖葫芦
public void buyProduct(int type) {
Product product = factory.createProduct(type);
product.prepare();
product.chuanChuan();
product.createSweetener();
product.castSweetenerToChuan();
System.out.println("制作完成,欢迎您的购买");
}
}
看完了ProductStore的代码你可能会有一个疑问,问什么简单工厂只负责了对象的创建,为什么没有把具体的制作流程都包含进简单工厂中,直接为店铺生产出一种符合客户要求的糖葫芦呢?其实如果保证制作流程不变的情况下,也可以那么做,虽然违背了要让类尽量保持单一责任的原则吧,不过不推荐那样干,毕竟简单工厂模式目的就是为了封装对象的创建。
我们来测下客户购买糖葫芦
public class SimpleFactoryTest {
public static final int SHANZHA = 0; // 山楂原料
public static final int ORANGE = 1; // 橘子原料
public static final int RICE = 2; // 糯米原料
public static void main(String[] args) {
ProductStore productStore = new ProductStore(new SimpleFactory());
productStore.buyProduct(SHANZHA);
}
}
到这里我们已经介绍完了简单工厂,相信你已经对于它有了一定的认识,虽然简单工厂的使用简单方便,但却不具备弹性,比如,如果SimpleFactory这个工厂并不能满足我的所有需求,我需要再添加一个工厂SimpleFactory二号,是不是还需要更改ProductStore的代码啊,这就违背了开放-关闭原则,我们应该针对接口编程,而不是针对这个SimpleFactory具体实现进行编程,解决办法可以是定义一个工厂接口,使ProductStore持有抽象引用。不要急,接着往下看吧,其实这种情况工厂方法为我们提供了更好的解决办法。
关于工厂方法模式的定义,我就直接引用HeadFirst了:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
还是以上面的糖葫芦为例,由于糖葫芦卖的非常火爆,所有有好多的店铺想要加盟,但是由于地理位置原因,客户的口味和运输成本的问题,一家工厂已经满足不了我们的全部需求啦,我们现在需要在加盟店本地开建工厂。
这里的需求决定要开建多家工厂,并不是说明工厂方法就得需要多个具体工厂类,只开建一家工厂是完全可以的,看完下面之后,你会发现只有一个具体创建者依然很有用。
这是一个糖葫芦店铺的抽象类。
public abstract class ProductStore {
// 购买糖葫芦
public void buyProduct(String type) {
Product product = createProduct(type);
if(product == null){
System.out.println("当前暂不提供该种类糖葫芦");
return;
}
product.prepare();
product.chuanChuan();
product.createSweetener();
product.castSweetenerToChuan();
System.out.println("制作完成,欢迎您的购买");
}
// 将具体的小店的实例化过程推迟到子类
abstract Product createProduct(String type);
}
抽象的目的,就是为了让子类决定要实例化的类是哪一个,ProductStore类只依赖Product,它并不知道要操作的具体糖葫芦种类是什么,这就让产品的实现从使用中解了耦。createProduct(String type)方法,就是我们上面定义中所说的创建对象的接口。
这是一个抽象的糖葫芦类。
public abstract class Product {
// 选取原料,包括山楂、橘子、糯米、哈密瓜、西瓜等
abstract void prepare();
// 穿串
public void chuanChuan() {
System.out.println("将选取的原料穿串");
}
// 熬制调料
public void createSweetener(){
System.out.println("熬制白糖调料");
}
// 浇在串上冷却
public void castSweetenerToChuan() {
System.out.println("将调料汁浇在串上并冷却");
}
}
这是一个记录具体糖葫芦种类的一个枚举。
public enum ProductType {
ShanZhaType("精品山楂"), OrangeType("精品橘子"), Rice("糯米")
, Hamimelon("哈密瓜"), Watermelon("西瓜");
String type;
private ProductType(String type) {
this.type = type;
}
}
这是北方的一家加盟糖葫芦店。
public class NorthProductStore extends ProductStore {
@Override
Product createProduct(String type) {
if(ProductType.ShanZhaType.type.equals(type)){
return new ShanZhaProduct();
}
else if(ProductType.OrangeType.type.equals(type)){
return new OrangeProduct();
}
else if(ProductType.Rice.equals(type)){
return new RiceProduct();
}
return null;
}
}
是不是看着代码非常眼熟,这不就是我们上面的简单工厂嘛,如果仔细比较你会发现还是很有区别的:简单工厂中使用的工厂是独立的类,或者是一个工厂接口的具体实现,跟ProductStore类是组合的关系,而这个NorthProductStore类是直接从ProductStore类扩展而来的。
这是具体的山楂糖葫芦。
public class ShanZhaProduct extends Product{
@Override
void prepare() {
System.out.println("选取精品山楂");
}
}
这个是具体的橘子糖葫芦.
public class OrangeProduct extends Product {
@Override
void prepare() {
System.out.println("选取精品橘子");
}
}
这是具体的糯米糖葫芦。
public class RiceProduct extends Product {
@Override
void prepare() {
System.out.println("选取精品糯米,并揉成饭团");
}
}
即使只有上面这一家加盟店是不是工厂方法模式依然很有用,它让商店和工厂中间解耦,当需要添加工厂时,又不必更改商店代码,符合对扩展开放,对修改关闭。
这是西部地区的一家加盟糖葫芦店。
public class WestProductStore extends ProductStore{
@Override
Product createProduct(String type) {
if(ProductType.Hamimelon.type.equals(type)){
return new HamimelonProduct();
}
else if(ProductType.Watermelon.type.endsWith(type)){
return new WatermelonProduct();
}
else if(ProductType.ShanZhaType.type.equals(type)){
return new ShanZhaProduct();
}
return null;
}
}
这是一个具体的哈密瓜糖葫芦。
public class HamimelonProduct extends Product {
@Override
void prepare() {
System.out.println("选取精品哈密瓜");
}
}
这是一个具体的西瓜糖葫芦。
public class WatermelonProduct extends Product {
@Override
void prepare() {
System.out.println("选取精品西瓜");
}
}
我们来测下客户购买糖葫芦。
public class FactoryMethodPatternTest {
public static void main(String[] args) {
// 这是北方的加盟糖葫芦店
ProductStore pStoreNorth = new NorthProductStore();
// 购买山楂糖葫芦
pStoreNorth.buyProduct(ProductType.ShanZhaType.type);
// 这是西方的加盟糖葫芦店
ProductStore pStoreWest = new WestProductStore();
// 购买哈密瓜糖葫芦
pStoreWest.buyProduct(ProductType.Hamimelon.type);
}
}
关于抽象工厂模式的定义,我就直接引用Head First了:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
还是以上面的糖葫芦为例,我们知道在糖葫芦的制作过程中,包括制作糖料这个步骤,现由于加盟店的增多,在制作糖料的原料使用上出现了很多问题,为了加强管理,挽回损失,所以总部决定在加盟店本地建设原料工厂,为加盟店提供专用水,白糖和芝麻。
这是一个糖葫芦店铺的抽象类。
public abstract class ProductStore {
//购买糖葫芦
public void buyProduct(String type) {
Product product = createProduct(type);
if(product == null){
System.out.println("当前暂不提供该种类糖葫芦");
return;
}
product.prepare();
product.chuanChuan();
product.createSweetener();
product.castSweetenerToChuan();
System.out.println("制作完成,欢迎您的购买");
}
// 将具体的小店的实例化过程推迟到子类
abstract Product createProduct(String type);
}
这是一个抽象的糖葫芦类。
public abstract class Product {
// 选取原料,包括山楂、橘子、糯米、哈密瓜、西瓜等
abstract void prepare();
// 穿串
public void chuanChuan() {
System.out.println("将选取的原料穿串");
}
/**
* 熬制调料。
*
* 我们将创建一个抽象工厂,
* 负责创建熬制糖料所需要的全部原料对象。
*/
abstract void createSweetener();
// 浇在串上冷却
public void castSweetenerToChuan() {
System.out.println("将调料汁浇在串上并冷却");
}
}
和上面不同的是,当前类熬制调料方法是抽象方法,因为不同的加盟店可能提供的原料不同,所以熬成的调料也就会有所差异
这是一个记录糖葫芦种类的枚举。
public enum ProductType {
ShanZhaType("精品山楂"), OrangeType("精品橘子"), Rice("糯米")
, Hamimelon("哈密瓜"), Watermelon("西瓜");
String type;
private ProductType(String type) {
this.type = type;
}
}
这是一个抽象工厂。
public interface IAbstractFactory {
// 拿到温水
WaterRawMaterial createWater();
// 获取白糖
SugarRawMaterial createSugar();
// 获取芝麻
SesameRawMaterial createSesame();
}
为工厂定义一个接口,这个接口负责创建熬制糖料所需要的全部原料对象。当需要创建产品家族和想让制造的相关产品集合起来时,可以优先选择抽象工厂。
这是一个专门给北方糖葫芦加盟店提供原料的具体工厂
public class NorthFactory implements IAbstractFactory {
@Override
public WaterRawMaterial createWater() {
return new WaterRawMaterial();
}
@Override
public SugarRawMaterial createSugar() {
return new SugarRawMaterial();
}
@Override
public SesameRawMaterial createSesame() {
return new SesameRawMaterial();
}
}
这是原料清水类。
public class WaterRawMaterial {
public WaterRawMaterial() {
System.out.print("加清水");
}
}
这是原料白糖类。
public class SugarRawMaterial {
public SugarRawMaterial(){
System.out.print(" 加白糖 ");
}
}
这是原料芝麻类。
public class SesameRawMaterial {
public SesameRawMaterial() {
System.out.println("加芝麻");
}
}
这是北方的一家具体的加盟店。
public class NorthProductStore extends ProductStore {
// 持有一个抽象工厂的引用
IAbstractFactory factory;
@Override
Product createProduct(String type) {
factory = new NorthFactory();
if(ProductType.ShanZhaType.type.equals(type)){
return new ShanZhaProduct(factory);
}
else if(ProductType.OrangeType.type.equals(type)){
return new OrangeProduct(factory);
}
else if(ProductType.Rice.equals(type)){
return new RiceProduct(factory);
}
return null;
}
}
这是山楂糖葫芦。
public class ShanZhaProduct extends Product{
IAbstractFactory factory;
public ShanZhaProduct(IAbstractFactory factory) {
this.factory = factory;
}
@Override
void prepare() {
System.out.println("选取精品山楂");
}
@Override
void createSweetener() {
factory.createWater();
factory.createSugar();
factory.createSesame();
}
}
让糖葫芦类与具体的原料类解耦。工厂方法模式通过继承,扩展自一个类,让子类决定要实例化的类是哪一个,从而达到解耦目的,而抽象工厂模式,通过对象的组合,即将定义了相关或依赖的对象家族接口,实例化之后,传入针对抽象类型所写的代码中,从而使客户从所使用的实际具体产品中解耦。
这是橘子糖葫芦。
public class OrangeProduct extends Product {
IAbstractFactory factory;
public OrangeProduct(IAbstractFactory factory) {
this.factory = factory;
}
@Override
void prepare() {
System.out.println("选取精品橘子");
}
@Override
void createSweetener() {
factory.createWater();
factory.createSugar();
factory.createSesame();
}
}
这是糯米糖葫芦。
public class RiceProduct extends Product {
IAbstractFactory factory;
public RiceProduct(IAbstractFactory factory) {
this.factory = factory;
}
@Override
void prepare() {
System.out.println("选取精品糯米,并揉成饭团");
}
@Override
void createSweetener() {
factory.createWater();
factory.createSugar();
factory.createSesame();
}
}
这个例子只提供了北方糖葫芦加盟店和北方原料工厂,你可以自己轻易扩展出西方糖葫芦加盟店和西方原料工厂类,因为他们都是松耦合的,可以轻易扩展.
我们来测下客户购买糖葫芦
public class AbstractFactoryPatternTest {
public static void main(String[] args) {
// 创建一个北方加盟糖葫芦店
ProductStore pStore = new NorthProductStore();
// 购买一个橘子糖葫芦
pStore.buyProduct(ProductType.OrangeType.type);
}
}