设计模式2.0-工厂模式
先来回忆一下设计原则:
设计原则 | 解释 |
---|---|
开闭原则 | 对扩展开放,对修改关闭。 |
依赖倒置原则 | 通过抽象使各个类或者模块不相互影响,实现松耦合。 |
单一职责原则 | 一个类、接口、方法只做一件事 |
接口隔离原则 | 尽量保证接口的纯洁性,客户端不应该依赖不需要的接口。 |
迪米特法则 | 又叫最少知道原则,一个类对其所依赖的类知道得越少越好。 |
里氏替换原则 | 子类可以扩展父类的功能但不能改变父类原有的功能。 |
合成复用原则 | 尽量使用对象组合、聚合,而不使用继承关系达到代码复用的目的。 |
工厂模式详解
工厂模式的历史由来
在现实生活中我们都知道,原始社会自给自足(没有工厂)、农耕社会小作坊(简单工 厂,民间酒坊)、工业革命流水线(工厂方法,自产自销)、现代产业链代工厂(抽象工厂, 富士康)
简单工厂模式
简单工厂模式(Simple Factory Pattern)是指由一个工厂对象决定创建出哪一种产品类的实例,但它不属于 GOF,23 种设计模式简单工厂适用于工厂类负责创建的对象较少的场景,且客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心。接下来我们来看代码
我们可以定义一个课程标准 ICar接口:
/**
* 汽车接口
*/
public interface ICar {
void run();
}
/**
* 奥迪类
*/
public class AudiCar implements ICar {
@Override
public void run() {
System.out.println("奥迪跑起来了");
}
}
public static void main(String[] args) {
ICar iCar=new AudiCar();
iCar.run();
}
看上面的代码,父类引用指向子类AudiCar()的引用,应用层代码需要依赖AudiCar,如果业务扩展,我继续增加 CadillacCar甚至更多,那么我们客户端的依赖会变得越来越臃肿。因此,我们要想办法把这种依赖减弱,把创建细节隐藏。虽然目前的代码中,我们创建对象的过程并不复杂,但从代码设计角度来讲不易于扩展。现 在,我们用简单工厂模式对代码进行优化。
/**
* 凯迪拉克类
*/
public class CadillacCar implements ICar {
@Override
public void run() {
System.out.println("凯迪拉克跑起来了");
}
}
创建工厂类SimpleFactory,下面给出了三种创建工厂方法
/**
* 创建实例
* @param type
* @return
*/
public ICar create(String type){
if(StringUtils.isEmpty(type)){
return null;
}
if("Audi".equals(type)){
return new AudiCar();
}else if("Cadillac".equals(type)){
return new CadillacCar();
}else {
return null;
}
}
/**
* 通过反射创建,不修改代码逻辑
* @param className
* @return
*/
public ICar createByclazz(String className){
try {
if (!(null == className || "".equals(className))) {
return (ICar) Class.forName(className).newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* 通过类实例化,方法参数是字符串,可控性有待提升,而且还需要强制转型
* @param clazz
* @return
*/
public ICar create(Class<? extends ICar> clazz){
try {
if (null != clazz) {
return clazz.newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
客户端调用代码
/**
* 简单工厂模式测试类
* 优点:把创建代码封装到工厂里,调用者感应不到,只需要传入响应的类型即可
* 缺点:不符合开闭原则,没增加一个产品都要修改工厂代码,不易于扩展
*/
public class SimpleFactoryTest {
public static void main(String[] args) {
SimpleFactory simpleFactory=new SimpleFactory();
ICar iCar= simpleFactory.create("Audi");
iCar.run();
//通过反射调用
ICar iCar1=simpleFactory.createByclazz("com.design.pattern.common.AudiCar");
iCar1.run();
ICar iCar2=simpleFactory.create(AudiCar.class);
iCar2.run();
}
}
我们来看一下类图:
简单工厂模式在 JDK 源码也是无处不在,现在我们来举个例子,例如 Calendar 类,看Calendar.getInstance()方法,下面打开的是 Calendar 的具体创建类:
private static Calendar createCalendar(TimeZone zone,Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
还有一个大家经常使用的 logback,我们可以看到 LoggerFactory 中有多个重载的方法getLogger():
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public static Logger getLogger(Class clazz) {
return getLogger(clazz.getName());
}
简单工厂也有它的缺点:工厂类的职责相对过重,不易于扩展过于复杂的产品结构。