简单工厂 + 工厂方法 + 抽象工厂
      看了十几篇博客,每篇基本上都能有个自己的解释,我汇总这些内容,重新梳理整理了一番,以形成自己的理解。
      简单工厂模式其实不算23种设计模式之一,它是一个非常简化版本的工厂。
      本文类图均参考: http://www.cnblogs.com/zhangchenliang/p/3700820.html  
      这里只有一个工厂对象SimpleFactory,负责创建多个AbstractProduct类型具体产品实例。
public class SimpleFactory {
public static void main(String[] args) {
Car car = CarFactory.createCar("Audi");
car.setBrand("Audi");
//生产
car.produce();
}
}
abstract class Car{
private String brand;
private String series;//暂时用不到
public abstract void produce(); public void setBrand(String brand) {
this.brand = brand;
}
public void setSeries(String series) {
this.series = series;
}
public String getBrand() {
return brand;
}
public String getSeries() {
return series;
}
}
//具体产品
class Audi extends Car{
public void produce(){
System.out.println("produce : " + this.getBrand());
}
}
class Bmw extends Car{
public void produce(){
System.out.println("produce : " + this.getBrand());
}
}
//简单工厂
class CarFactory {
public static Car createCar(String car){
Car c = null;
if("Audi".equalsIgnoreCase(car))
c = new Audi();
else if("Bmw".equalsIgnoreCase(car))
c = new Bmw();
return c;
}
}
      在这个案例中,我要生产一辆车,不再需要自己new一个车,而只需要传入一个类型(品牌),根据类型来创建车。的确隐藏了实现细节。但是问题在于:
      使用者首先必须确定要create哪种车,然后传入类型,然后在工厂里又if-else判断了到底是哪种车,违背了开闭原则,我需要增加对其他子类的支持,必须修改代码,增加else判断分支。毫无疑问是有问题的,看起来这个if-else很多余,我既然已经告诉你我要奥迪车,你为什么内部自己还判断了呢。当然,反射是可以避免if-else 的,但是要使用Class.forName() 必须要传入类的完全限定名,这点在使用中是非常麻烦的。
      如何解决?
      既然我在工厂里面也要判断哪种品牌,那为什么我不将判断拿出来呢?对于奥迪车,我建立一个小工厂专门生产奥迪车,而奔驰、宝马我也各自建一个工厂去生产。这样,在客户调用的时候,你只需要告诉我你准备用那个公司的生产车间,我就能生产这个品牌的汽车,这也就是工厂方法模式。
    工厂方法模式,为了避免if-else判断,干脆再封装一层工厂
     以此图为例
      client持有AbstractFactory的引用,AbstractFactory可创建具体工厂对象ConcreteFactory1,ConcreteFactory1。
每个具体工厂可生产具体的产品 ConcreteProduct1,ConcreteProduct2。这两个具体产品可抽象为AbstractProduct。因此用抽象父类接受具体子类对象。
public class FactoryMod {
@Test
public void test() {
CarFactory factory = new AudiFactory();
factory.createCar().produce();
}
abstract class Car{
private String brand;
private String series;//暂时用不到
public abstract void produce();
public void setBrand(String brand) {
this.brand = brand;
}
public void setSeries(String series) {
this.series = series;
}
public String getBrand() {
return brand;
}
public String getSeries() {
return series;
}
}
class Audi extends Car{
public void produce(){
System.out.println("produce : " + this.getBrand());
}
}
class Bmw extends Car{
public void produce(){
System.out.println("produce : " + this.getBrand());
}
}
class AudiFactory implements CarFactory{
public Car createCar(){
Car audi = new Audi();
audi.setBrand("audi");
return audi;
}
}
class BmwFactory implements CarFactory{
public Car createCar(){
Car bmw = new Bmw();
bmw.setBrand("bmw");
return bmw;
}
}
//工厂
interface CarFactory {
Car createCar();
}
}
 
      此处我建了两个工厂,分别生产不同品牌的汽车,这样客户端调用时,只要指明了是哪个工厂,就不必再有if-else判断。如果我们增加奔驰生产的功能,只需要在Car下面增加奔驰类,并增加生产奔驰类的车间,无需改动代码,就可以生成奔驰车。
而且我还可以指定生产什么型号的汽车:
class AudiFactory implements CarFactory{
public Car createCar(String series){
Car audi = new Audi();
audi.setBrand("audi");
audi.setSeries(series);
return audi;
}
}
class BmwFactory implements CarFactory{
public Car createCar(String series){
Car bmw = new Bmw();
bmw.setBrand("bmw");
bmw.setSeries(series);
return bmw;
}
}
//我指定汽车型号,客户端想要生产汽车的时候必须告诉我型号,然后我就可以给你相应型号的汽车。
interface CarFactory {
Car createCar(String series);
}
      上面的代码就可以实现生产多个车型
      但是这种写法其实是有问题的,或者说适用性不强。因为这里我是吧车型当成了Car的一个内部属性,但是实际上,车型可能是一个单独的属于奥迪车的子类。
那么如果我不仅要生产奥迪,我还要指定车型,比如奥迪A4,奥迪A6呢?工厂方法如何实现?
public class FactoryMod2 {
@Test
public void test() {
CarFactory factory = new AudiFactory();
factory.createCar().produce();//生产奥迪汽车
factory.createCar("A4").produce();//生产奥都A4
}
//汽车都有自己的序列号
abstract class Car {
private String id = String.valueOf(Math.random());
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public abstract void produce();
}
//奥迪汽车还有自己的品牌名
class Audi extends Car {
private String brand = "Audi";
public void produce() {
System.out.println("produce : " + this.getBrand() + "," + this.getId());
}
public String getBrand() { return brand; }
}
//奥迪A4汽车除了品牌还有型号
class AudiA4 extends Audi {
private String series = "A4";
public void produce() {
System.out.println("produce : " + this.getBrand() + "," + this.getSeries() + "," + this.getId());
}
public String getSeries() { return series; }
}
class AudiA6 extends Audi {
private String series = "A6";
public void produce() {
System.out.println("produce : " + this.getBrand() + "," + this.getSeries() + "," + this.getId());
}
public String getSeries() { return series; }
}
class AudiFactory implements CarFactory {
//要判断客户要什么系列
public Car createCar(String series) {
Audi car = null;
if (series.equals("A4"))
return new AudiA4();
else if (series.equals("A6"))
return new AudiA4();
return null;
}
public Car createCar(){
return new Audi();
}
}
//简单工厂
interface CarFactory {
Car createCar(String series);
Car createCar();
}
}
      代码稍微复杂了一点,这里我省略了宝马工厂的创建,都是相似的,并且调整了继承体系。
      我们很容易在Audi下在派生子类AudiA4 和 AudiA6 没问题,在用户调用的时候我们要把用户要求的型号告诉车间,车间里判断用户到底是要哪个系列,我们就给他哪个系列。很好,我们实现了功能:
produce :Audi,0.5005191840367693
produce :Audi,A4,0.1326089233983656
      但是if-else的问题又来了,我们在奥迪生产车间里,又一次遇到了if判断,判断到底是哪种系列。就像一开始我们用简单工厂模式一样。
随着需求的复杂度提高,单一的奥迪工厂已经无法轻松写意的生产各种型号的奥迪汽车,它的produce方法开始负责A4和A6汽车的生产,这违背了单一职责原则。进而违背了开闭原则。
      怎么办?
      我们还是从刚才简单工厂到工厂方法的转变上,试着寻求消除if-else的方式,前面在简单工厂中,调用者已经指明我要的是奥迪汽车,但是工厂里还是判断了一次,因为工厂意图承担多个创建责任。我们分析之后觉得应该把这个判断拿出来。也就是说让客户直接选择要创建哪种汽车,但是又不能直接new,所以我们就在原先工厂的地方再用一次工厂,将原先的职责划分开来,形成奥迪工厂和宝马工厂,然后调用者直接拿奥迪工厂生产奥迪汽车,现在因为需求变更,奥迪工厂不得不承担多个生产责任,产生多个if-else。我们要干掉他,所以我们选择在奥迪工厂上,仿照上面的做法,再用工厂划分出奥迪A4产品线,奥迪A6产品线。
      我初步试了一下。
class AudiA4Factory implements CarFactory{
public Car createCar(){
return new AudiA4();
}
}
class AudiA6Factory implements CarFactory{
public Car createCar(){
return new AudiA6();
}
}
      发现这种基于垂直的继承体系,最好的办法就是直接使用父类工厂,直接创建子类,也就是说无论是奥迪还是奥迪A4,还是宝马,都直接用对应的AudiA4Factory,AudiA6Factory来生产。既然客户端知道确定的类型,就直接创建确定类型的工厂。当然这里看起来好像不太科学,如果垂直继承体系很深,那么不同各层级的工厂有些不太清晰。关于这种垂直继承体系,使用什么来创建比较好,此处不展开讨论,或许直接创建子类的工厂,子类的子类的工厂更好,如果要实现单一职责开闭原则的话,容易变化的就不能作为方法存在,比如生产各种型号的汽车,如果做成createAudiAt() createAudiA6()方法,就会出现之前的问题。
      我们还有个问题没有解决:抽象工厂怎么用?
     抽象工厂的应用场景,不是类似于这种垂直的产品体系,因为这种垂直的体系就只有一个产品等级结构。
      产品族与产品等级结构如图所示。
    
 
      产品等级结构就是一系列具有垂直继承体系的产品。产品族就是一系列没有继承关系的产品集合,比如手枪和子弹,鼠标和键盘。 
 
      通过工厂方法与抽象工厂方法的区别来理解抽象工厂模式:
    
      对于工厂方法:此处抽象产品类就是Car,派生的具体产品类就是Audi,Bmw。此处抽象工厂类就是CarFactory。具体工厂类就是AudiFactory.
根据前面的分析,抽象产品类可以派生多个具体产品类。抽象工厂类可以派生多个具体工厂类,而具体工厂类只能创建一个具体产品实例。多了就要违背开闭。
      对于抽象工厂:是有多个抽象产品类的,也就是多个产品族,例子有:汽车、飞机、航母;枪与子弹,鼠标与键盘等。是不同继承体系的产品。
多个抽象产品类可以抽象出多个具体产品类,比如雷蛇鼠标雷柏鼠标,键盘可能是雷蛇键盘雷柏键盘等。每个抽象工厂,可以派生多个具体工厂类,而每个具体工厂类可以创建多个产品实例,意思就是每个工厂都能生产鼠标和键盘,但是却有不同的工厂去生产不同的实例。
类图如下:
      现在有抽象工厂类AbstractFactory,它可以创建几个具体的工厂 ConcreteFactory1,ConcreteFactory2,具体的每个工厂都能生产具体的A产品和B产品,但是A,B产品并没有继承关系,它们是不同的产品等级体系,现在要增加一个产品族,只需要增加一个相应产品族的工厂和具体的产品,比如A3,B3。大师要增加一个新产品比如C,那么3个工厂都需要修改内容,以生产新的产品。
代码:
//抽象产品(Bmw和Audi同理)
abstract class BenzCar{
private String name; public abstract void drive(); public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//具体产品(Bmw和Audi同理)
class BenzSportCar extends BenzCar{
public void drive(){
System.out.println(this.getName()+"----BenzSportCar-----------------------");
}
}
class BenzBusinessCar extends BenzCar{
public void drive(){
System.out.println(this.getName()+"----BenzBusinessCar-----------------------");
}
} abstract class BmwCar{
private String name; public abstract void drive(); public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class BmwSportCar extends BmwCar{
public void drive(){
System.out.println(this.getName()+"----BmwSportCar-----------------------");
}
}
class BmwBusinessCar extends BmwCar{
public void drive(){
System.out.println(this.getName()+"----BmwBusinessCar-----------------------");
}
} abstract class AudiCar{
private String name; public abstract void drive(); public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class AudiSportCar extends AudiCar{
public void drive(){
System.out.println(this.getName()+"----AudiSportCar-----------------------");
}
}
class AudiBusinessCar extends AudiCar{
public void drive(){
System.out.println(this.getName()+"----AudiBusinessCar-----------------------");
}
} //抽象工厂
abstract class Driver3{
public abstract BenzCar createBenzCar(String car) throws Exception; public abstract BmwCar createBmwCar(String car) throws Exception; public abstract AudiCar createAudiCar(String car) throws Exception;
}
//具体工厂
class SportDriver extends Driver3{
public BenzCar createBenzCar(String car) throws Exception {
return new BenzSportCar();
}
public BmwCar createBmwCar(String car) throws Exception {
return new BmwSportCar();
}
public AudiCar createAudiCar(String car) throws Exception {
return new AudiSportCar();
}
}
class BusinessDriver extends Driver3{
public BenzCar createBenzCar(String car) throws Exception {
return new BenzBusinessCar();
}
public BmwCar createBmwCar(String car) throws Exception {
return new BmwBusinessCar();
}
public AudiCar createAudiCar(String car) throws Exception {
return new AudiBusinessCar();
}
} //老板
public class BossAbstractFactory { public static void main(String[] args) throws Exception { Driver3 d = new BusinessDriver();
AudiCar car = d.createAudiCar("");
car.drive();
}
}
      上面的代码比较清楚的展示了抽象方法的工作原理。
      此处的抽象工厂是Driver3,具体工厂是BussinessDriver,SportDriver等。抽象产品是AudiCar,具体产品是AudiSportCar和AudiBussinessCar。
因此对于抽象工厂,支持多个产品族,要增加一个产品族很容易,只需要增加一个具体工厂,比如生产SUV型轿车。只需要实现一个SUVDriver,但是要增加一种汽车产品线,比如大众。就必须在所有的产品族工厂里增加createVolkswagenCar,如果用鼠标键盘举例子的话,就是每个厂商(雷柏雷蛇赛睿)就是具体的工厂,是产品族。而每中产品,鼠标键盘就是产品等级体系。
    有不清楚的可以继续参考:
 
总结:
1.为什么用工厂方法?
  概括起来大致有以下的说法:
  将对象的实例化归集起来,避免一个类到处实例化对象,一旦需求变更导致霰弹式修改,如果以后要改,直接在工厂里改就行了。
  类的某些实例化需要有比较多的初始化设置,放到工厂里可以封装代码,而不至于到处都是重复的初始化代码。
  从解耦的角度考虑,不应该硬编码,其实Spring才是最大的工厂,管理所有的代码,实现所有代码的解耦。springIOC有什么好处,工厂差不多也有这些好处。
  将对象本身的职责,对象的创建创建逻辑,使用逻辑隔离开来。
  -------------------------------------------------
  也有认为工厂模式华而不实的。
  也有认为工厂是C++时代设计模式遗留到java中的产物,其实并不一定需要,java通过反射可以方便的拿到所需要的实例。
  不过普通程序员在java开发中广泛运用spring框架,多数时候不太需要,而且对于javaWeb这种CRUD的项目来说,也不需要太多的设计模式。最后就是,许多人都没有找到适合工厂模式的应用场景,不能为了模式而套模式,导致过度设计。工厂方法到底有没有用,我还不能下定论,或许5年之后,我能理解这个模式更深层次的内涵。
 
2.何时使用工厂方法。
  个人浅薄地认为,只要有new的地方,都可以考虑一下是否使用工厂,对于简单很少有变动的情况,可以考虑用简单的工厂,毕竟结构要清洗一点,也没有特别大的变动。对于产品等级体系发达的情况,优先用工厂模式。对于产品族发达,而产品等级体系固定的情况,用抽象工厂。有些时候这几种工厂可以组合使用,目的其实都是一样,遵循6个设计原则,实现高内聚低耦合。

java设计模式之工厂方法探究的更多相关文章

  1. Java设计模式之工厂方法模式(转) 实现是抽象工厂?

    Java设计模式之工厂方法模式 责任编辑:覃里作者:Java研究组织   2009-02-25   来源:IT168网站   文本Tag: 设计模式 Java [IT168 技术文章]         ...

  2. Java设计模式系列-工厂方法模式

    原创文章,转载请标注出处:<Java设计模式系列-工厂方法模式> 一.概述 工厂,就是生产产品的地方. 在Java设计模式中使用工厂的概念,那就是生成对象的地方了. 本来直接就能创建的对象 ...

  3. java设计模式(二)---工厂方法模式

    2普通工厂方法模式 就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 2.1创建接口 /** * 发送接口 * Created by mrf on 2016/2/25. */ public ...

  4. Java设计模式 之 工厂方法模式

    1. 使用设计模式的好处:可提高代码的重复性,让代码更容易被他人理解,保证代码的可靠性. 2. 工厂模式定义:就是创建一个工厂类来创建你需要的类,工厂模式包括工厂模式和抽象工厂模式,抽象工厂模式是工厂 ...

  5. JAVA设计模式之工厂方法模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述工厂方法模式的: 工厂方法模式是类的创建模式,又叫做虚拟构造子(Virtual Constructor)模式或者多态性工厂(Polymor ...

  6. Java 设计模式(四)-工厂方法模式 (FactoryMethod Pattern)

    1     概念定义 1.1   定义 定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 1.2   类型 创建类模式 2     原理特征 2.1   类 ...

  7. 【java】 java 设计模式(1):工厂方法模式(Factory Method)

    工厂方法模式分为三种: 1.普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建.首先看下关系图: 举例如下:(我们举一个发送邮件和短信的例子) 首先,创建二者的共同接口:   p ...

  8. java设计模式学习 ----- 工厂方法模式(Factory Method)

    工厂方法模式(Factory Method) 工厂方法模式分为三种:普通工厂模式.多个工厂方法模式.静态工厂方法模式 普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 关系图 ...

  9. Java设计模式(2)——工厂方法模式

    工厂方法模式同样属于类的创建型模式又被称为多态工厂模式.工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中.核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色, ...

随机推荐

  1. 在db2数据库上模拟死锁场景 还是z上的

    如果条件允许,起两个线程互相抢资源就行了,但问题是,时间上还需要同步,要做到完美控制,还得加其他逻辑,忒费事,所以可以用下面的办法: 在目标表上直接加个锁……简单,粗暴,直接……很好…… LOCK T ...

  2. 关于tarjan算法的理解

    理解要点如下 理解LOW[i]数组的迭代过程.. low[u]=min(dfn[v],dfn[u],low[v]); 理解这个..如果有环..那么后代就可以更新祖先 那么low[v]就有用了.. 那么 ...

  3. winform用户控件、动态创建添加控件、timer控件、控件联动

    用户控件: 相当于自定义的一个panel 里面可以放各种其他控件,并可以在后台一下调用整个此自定义控件. 使用方法:在项目上右键.添加.用户控件,之后用户控件的编辑与普通容器控件类似.如果要在后台往窗 ...

  4. Android onActivityResult没响应

    原因: 1.当MainActivity2的启动模式为SingleTask时, 系统为自动返回setResult(Activity.RESULT_CANCELED). 2.当为MainActivity2 ...

  5. Html中鼠标悬停显示二级菜单的两种方法

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. JavaScript闭包深入解析

    for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); } --上面这段 ...

  7. nodejs随记04

    aes加密 资料 简介; 例子; process 改变工作目录: process.chdir(path); 路径计算 例子 获取调用执行所在文件地址 function getCaller() { tr ...

  8. 转载:Tomcat的JVM设置和连接数设置

    Windows环境下修改“%TOMCAT_HOME%\bin\catalina.bat”文件,在文件开头增加如下设置:set JAVA_OPTS=-Xms256m -Xmx512m Linux环境下修 ...

  9. Unity Standard Assets 简介之 Vehicles

    这篇介绍载具资源包Vehicles. 主要包含Aircraft(飞行器)和Car(车辆)两部分,两个文件夹里分别有AircraftGuidelines.txt和CarGuidelines.txt对相关 ...

  10. pointers on c (day 1,chapter2)

    交叉编译器(cross complier)就是在一台机器上运行,但它所产生的可执行代码运行在不同类型的机器上. 翻译阶段由几个步骤组成,组成一个程序的每一(有可能有多个)源文件通过编译过程分别转换成目 ...