又是新的一天,今天是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今天就先到这里了,
网友评论