玩转设计模式

第二章——工厂方法模式

2017-10-24  本文已影响0人  博尔特uncle

又是新的一天,今天是19大闭幕式,呵呵我的股票长了点,唉总算是收回了点成本,我是实践中深刻体会到了这句话的内涵,股市有风险,入市需谨慎;还是少玩的好;今天我们来玩玩工厂方法模式.
先看下定义:
工厂方法模式定义:Define an interface for creating an object,but let subclasses decide which class to
instantiate.Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的
接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。),

工厂方法模式是一个应用最广泛的设计模式,也是一个高度解耦的设计模式:咱们今天从代码中展开,相信任何一个有经验的程序员一看就了然。
今天同事准备买电脑让我帮他推荐个电脑,咱们就以电脑为例吧:

  public interface Computer {
/**
 * 电脑型号
 * 
 * @return
 */
public String getModelNumber();

/**
 * 电脑出厂价
 * 
 * @return
 */
public String getComputerPrice();

/**
 * 电脑的基本配置
 * 
 * @return
 */
public String getComputerDesc();
     }


 public class InspirationComputer implements Computer {
@Override
public String getModelNumber() {
    return "灵越系列Ins15E-3725";
}

@Override
public String getComputerPrice() {
    
    return "3333元";
}

@Override
public String getComputerDesc() {

    return "适用场景 家庭影音 女性定位 轻薄便携 学生 商务办公 高清游戏 家庭娱乐";
}
   }
     public class VostroComputer implements Computer {

@Override
public String getModelNumber() {
    // TODO Auto-generated method stub
    return "Vostro 5459-1528";
}

@Override
public String getComputerPrice() {
    // TODO Auto-generated method stub
    return "¥ 4499.00";
}

@Override
public String getComputerDesc() {
    // TODO Auto-generated method stub
    return "适用场景     商务办公 家庭娱乐";
}}

戴尔工厂里生产电脑;那么电脑有型号有价格,和基本配置定位;这是产品的共有特性;我们用一个接口来统一定义这些方法;注意面向接口编程一个好处就是便于扩展;有了电脑就应该有工厂去生产:

            /**
              *  抽象工厂上层
             *
             */      
     public abstract class AbsrtactComputerFactory {
// 限制传入类型只能是Computer的子类
   public abstract <T extends Computer> T createComputer(Class<T> c);
       }

    /**
    * 具体的工厂方法实现,目的就是生产电脑
   * 
    * @author Administrator
  *
  */

  public class ComputerFactory extends AbsrtactComputerFactory {
@Override
public <T extends Computer> T createComputer(Class<T> c) {
    Computer computer = null;
    try {
        computer = (T) Class.forName(c.getName()).newInstance();
    } catch (Exception e) {

        e.printStackTrace();
    }

    return (T) computer;
}}

好了现在我们有了工厂了,可以开工了:

    public static void main(String[] args) {
    // TODO Auto-generated method stub
    AbsrtactComputerFactory compFactory = new ComputerFactory();
    Computer inspirationComputer = compFactory.createComputer(InspirationComputer.class);
    System.out.println(inspirationComputer.getComputerDesc() + "\n" + inspirationComputer.getModelNumber() + "\n"
            + inspirationComputer.getComputerPrice());
    Computer vostroComputer = compFactory.createComputer(VostroComputer.class);
    System.out.println(vostroComputer.getComputerDesc() + "\n" + vostroComputer.getModelNumber() + "\n"
            + vostroComputer.getComputerPrice());
}
图片.png

由图可见,我们构造了一个工厂生产了两台不同型号的电脑,简单吧,这就是工厂方法模式的通用用法。

列举下他的优点吧:
1.良好的封装性,代码结构清晰;
一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合;对于调用者来说我们只需要知道生产什么型号的电脑,知道类名就可以,不需要关注这个类怎么创建的.
2.扩展性非常优秀;
就代码而言,如果我们需要生产一个X型号的电脑,具有特殊特性不具备公共特性怎么办?我们只需要增加一个X型即可,并且再此进行特性扩展,不需要修改工厂生产的上层封装;
如果盘子大了怎么办?遇到业务代码很多,我们是不是可以考虑开辟不同的工厂专司其职呢,比如我们想戴尔灵越系列专职由一个工厂生产,Vostro系列专由一个工厂生产,结构更清晰,我们完全可以创建不同的工厂去做,这也是面向接口编程的好处,但是也有弊端,因为维护成本就高了,产品和工厂是一一对应的关系,有几个产品就要创建几个工厂.

3.屏蔽产品类
产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不会发生变化,也就是从上到下的过程,

以上是工厂方法模式的标准写法,下面我们看看工厂方法模式的几个变种使用:

1.简单工厂模式:
每次都要创建一个工厂太费劲了,我们把工厂的抽象去掉,创建的方法改成静态:

   public class ComputerFactory  {
   public static <T extends Computer> T createComputer(Class<T> c) {
    Computer computer = null;
    try {
        computer = (T) Class.forName(c.getName()).newInstance();
    } catch (Exception e) {

        e.printStackTrace();
    }

    return (T) computer;
}
 }
        



     public static void main(String[] args) {
    // TODO Auto-generated method stub
    Computer comp=ComputerFactory.createComputer(InspirationComputer.class);
      //        AbsrtactComputerFactory compFactory = new ComputerFactory();
 //     Computer inspirationComputer = compFactory.createComputer(InspirationComputer.class);
//      System.out.println(inspirationComputer.getComputerDesc() + "\n" + inspirationComputer.getModelNumber() +           "\n"
                //              + inspirationComputer.getComputerPrice());
            //      Computer vostroComputer = compFactory.createComputer(VostroComputer.class);
         //     System.out.println(vostroComputer.getComputerDesc() + "\n" + vostroComputer.getModelNumber() + "\n"
       //               + vostroComputer.getComputerPrice());
    System.out.println(comp.getComputerDesc() + "\n" + comp.getModelNumber() + "\n"
            + comp.getComputerPrice());
} 

现在是不是清爽了许多,这种方式应用还是很广泛的,叫做简单工厂模式,但是工厂类的扩展就不那么方便了

2.多工厂模式:我们还是看代码

   public abstract class AbsrtactComputerFactoryzz {
public abstract Computer createComputer();}
  


    public class InspirationFactory extends AbsrtactComputerFactoryzz {

@Override
public Computer createComputer() {
    return new InspirationComputer();
}}

我们创建了抽象工厂类,工厂里的方法还是生产电脑,这一点不容置疑,就像多年前一个前辈说的,人在黑板上花圆,现在有人,黑板,圆三个对象,画圆的方法应该谁提供呢?很明显是圆,圆包含半径,圆心,自然是它;
由于是多工厂模式,自然各个工厂各司其职,在子类中写死这个工厂干什么的就行了;

3.替代单例模式

故名思议,这是一种可以用工厂方式做到单例设计模式的一种工厂变体,其实这是不明智的,开发有开发的规矩,不能使用一些非正常手段去实现通常写法,但是也要了解一下,我们看下代码:

   public class CommonFactoryTest {
private static CommonFactoryTest singleton;
static {
    try {
        Class cl = Class.forName(CommonFactoryTest.class.getName());
        // 获得无参构造
        Constructor constructor = cl.getDeclaredConstructor();
        // 设置无参构造是可访问的
        constructor.setAccessible(true);
        // 产生一个实例对象
        singleton = (CommonFactoryTest) constructor.newInstance();
    } catch (Exception e) {
        // 异常处理
    }
}

public static CommonFactoryTest getSingleton() {
    return singleton;
}}

我们用暴力反射技术,和classload的机制保证内存中只有一个实例存在,但是很明显效率是相当低的,不应该这么干。

4.延迟初始化,

一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用;
比如说我们不希望一个对象频繁销毁,想复用时候,比如做内存缓存的时候,但是应当注意内存泄漏的可能;比如Android开发中首页tab的fragment我们就可以用这种方式保存其产品的状态不被释放以利复用;下面看一下代码:

         public static final int PLAYSHARE_SFRAGMENT = 0;
public static final int DISCUSS_FRAGMENT = 1;
public static final int MARKET_FRAGMENT = 2;
public static final int CONSULTING = 3;
public static final int MY_FRAGMENT = 4;
private static SparseArray<BaseFragment> mFragments = new SparseArray<BaseFragment>();
public static BaseFragment createFragment(int position) {
    BaseFragment fragment = mFragments.get(position);
    if (fragment == null) {
        switch (position) {
            case PLAYSHARE_SFRAGMENT:
                fragment = new PlaySharesFragment();
                break;
            case DISCUSS_FRAGMENT:
                fragment = new DiscussFragment();
                break;
            case MARKET_FRAGMENT:
                fragment = new QuotationFragment();
                break;
            case CONSULTING:
                fragment = new ConsultingFragment();
                break;
            case MY_FRAGMENT:
                fragment = new MyFragment();
                break;
            default:
                break;
        }
        mFragments.put(position, fragment);
    }
    return fragment;
}

很明显,代码对我们需要的产品进行了缓存再利用;无论你用Map容器也好还是其他List集合也好,原理都一样都是希望对已有产品的再利用,这个方式应用也很广泛,但是应当注意缓存的释放;

工厂方法模式总结:
是典型的解耦框架,高层模块只需要知道产品的抽象类,其他的实
现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依
赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类

使用场景:
万物皆对象,工厂方法模式是用来生产对象,他可以替代new Object ,任何使用 new 对象的地方我们都可以用工厂模式替代,但是我们要考虑是否值得这么做,因为这样对于开发者不也是增加了代码复杂度吗?
Ok今天就先到这里了,

上一篇下一篇

猜你喜欢

热点阅读