为什么要用设计模式?
这是个好问题,可以说一大堆,啥系统的健壮性啊,易修改性啊,可扩展性啊?其实吧,就一句话,设计模式是在软件工程实践过程中,程序员们总结出的良好的编程方法。
好,那么既然使好的编程方法,那么各种大框架中肯定用了吧,那么我们来看看spring中哪些地方用到了设计模式?
再来看看spring四大块用到的设计模式
果然,一个如此牛逼的框架必须用了设计模式不是,那么作为程序员的你是不是应该来学一学设计模式呢?好,那现在我们就来看看工厂模式吧。
工厂模式
首先,工厂模式就是借鉴现实中工厂的经验,顾名思义,工厂就是用来生产产品呢,那么对于java而言,工厂就是一个工厂类,用来生产指定的产品类,话不多说,实践一下你就知道了。
首先说说场景:现在有一家制药公司需要有一个管理系统来记录它生产的药,那么我们可以把它想象成一个简单工厂模式来用代码实现。
需求分析:
1、药肯定不止一种,那么我们首先需要一个药的超类,然后有药的子类来继承它。
2、有一个工厂类来生产这些药。
3、客户端调用生产药。
/**
* 接口,药,产品
*/
public interface IDrug {
public void introduce();
}
/**
* @Author Darker
* @Descrption 补肾药
* @Date : Created in 15:40 2020-3-9
*/
public class BuShenDrug implements IDrug{
@Override
public void introduce(){
System.out.println("我是补肾药了,兄弟肾该补补了");
}
}
/**
* @Author Darker
* @Descrption 制药工厂
* @Date : Created in 15:43 2020-3-9
*/
public class DrugFactory {
public IDrug create(Class clazz){
try{
if(clazz!=null){
return (IDrug) clazz.newInstance();
}
}catch (Exception e){
System.out.println("无法识别class:"+e.getMessage());
}
return null;
}
}
准备好这些类以后,我们来进行客户端的调用!
/**
* @Author Darker
* @Descrption
* @Date : Created in 15:43 2020-3-9
*/
public class SimpleFactoryTest {
public static void main(String[] args) {
DrugFactory factory = new DrugFactory();
IDrug drug = factory.create(BuShenDrug.class);
drug.introduce();
}
}
运行之后我们可以看到,通过客户端调用,传入你想要生产的药的类,它便会自动生产出你需要的药,这便是一个简单工厂模式了。
接下来我们对简单工厂模式做一个总结:
/**
* 使用场景:
* 1.工厂类负责创建得对象较少
* 2.客户端只需要传入工厂类得参数,对于如何创建对象的逻辑不需要关心
* 优点:
* 只需要传入一个正确的参数就可以获得正确的对象,不需要知道具体细节
* 缺点:
* 工厂类的职责相对过重,增加新的产品时需要修改工厂类的判断逻辑,违背开闭原则
* 不易于扩展过于复杂的产品结构
*/
我们再来看看jdk中对简单工厂模式的使用,面试中如果被问到也可以说说哦。
Calendar类相信大家都很熟悉,但是你知道它用了简单工厂模式嘛?
//简单工厂
Calendar.getInstance();
进入方法,我们来看看它做了哪些事情。
第一步根据这两个默认参数创造一个日历类 。
//原方法
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;
}
第二步来了,看到了什么,和我们上面客户端调用的方式是不是一样,根据传进去的CalendarProvider.class来生产我们想要的日历类的提供者,如果生成了这个日历提供者就从中获得日历类,然后下面是一大串的逻辑判断,我们再进LocaleProviderAdaPter去看看。
第三步,果然,这就是一个工厂的抽象类,根据你传入的LocaleProviderAdapter的子类类型来生产你要的日历提供类,nice,原来我们经常用的日历类是这么生产出来的。
怎么样,发现了设计模式的应用了吧,好,那么再来想一想,既然有简单工厂模式,那么会不会有复杂工厂模式呢,没错,接下来由于业务的升级,或者说更加复杂,当然有,那就是工厂方法模式。
使用场景:工厂方法模式就是我不想再传入一些固定的参数了,要不然违反开闭原则,每次我加一个生产品就要去更改相应的代码,比如我现在想再加一种药,比如防脱药,是不是就要更改逻辑判断(当然上面是使用反射来避免了,但有时候不能用反射需要用到字段类型选择的时候就会出现这个问题),那么我们现在把工厂的生产方法单独提取出来,只有一个生产方法的方法工厂,你想要什么药,就要先有一个这个要的工厂,再从这个工厂里面来生产药。
//方法工厂,接口
public interface IMethodFactory {
public IDrug drug();
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 16:18 2020-3-9
*/
public class FangTuoFactory implements IMethodFactory {
@Override
public IDrug drug() {
return new FangTuoDrug();
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 16:16 2020-3-9
*/
public class BuShenFactory implements IMethodFactory {
@Override
public IDrug drug() {
return new BuShenDrug();
}
}
/**
* 接口,药,产品
*/
public interface IDrug {
public void introduce();
}
/**
* @Author Darker
* @Descrption 补肾药
* @Date : Created in 15:40 2020-3-9
*/
public class BuShenDrug implements IDrug{
@Override
public void introduce(){
System.out.println("我是补肾药了,兄弟肾该补补了");
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 15:56 2020-3-9
*/
public class FangTuoDrug implements IDrug{
@Override
public void introduce() {
System.out.println("我是防脱药,兄弟头发该保养了");
}
}
我们再来看看客户端调用。
public class MethodFactoryTest {
public static void main(String[] args) {
IMethodFactory fangTuoFactory = new FangTuoFactory();
IDrug drug = fangTuoFactory.drug();
drug.introduce();
}
}
接下来我们对方法工厂模式做一个总结:
/**
* 使用场景
* 1.创建对象需要大量重复的代码
* 2.客户端不依赖于产品类实例如何被创建,如何被实现。
* 3.一个类通过其字类来指定创建哪个对象
* 优点
* 1.只需要关心所需产品对应的工厂,无需关心创建细节
* 2.符合开闭原则,增加扩展性
* 缺点
* 1.类的个数过多,增加代码结构的复杂度
* 2.增加了系统的抽象性和复杂性
*/
这种方法工厂模式不管你药的产品怎么来增加,你只需要增加你的药的子类和对应的工厂的子类即可,不需要你再来关心客户端是否传对了参数,也不用关心你里面生产产品的判断逻辑了,完全符合开闭原则,同时易于扩展。
我们再来看看框架中的用法,比如我们经常使用的日志类,它就是典型的方法工厂模式。
//方法工厂模式
Logger logger = LoggerFactory.getLogger("");
第一步,首先我们点进这个方法,一直到这个工厂的超类,我们可以看出,这个就是一个提供获得日志方法的方法工厂类。
第二步,可以看到它下面有3个子类的日志工厂分别对应要生成日志子类的工厂,而我们用的方法getILoggerFactory(),就是返回我们的生产日志子类的工厂,然后根据这个子类的工厂直接生成对应的子类日志。
第三步,我们随意点进substituteLoggerFactory这个子类日志工厂,就能看到它是生产对应的sub日志的工厂了,好了,这就是方法工厂模式在log4j日志框架中的运用了。
使用场景: 当这个工厂进一步扩大,我已经不局限于生产这个公司的药了,我还要生产比如机器,比如包装,我不再是只提供一个生产药的方法了,那么我们的工厂此时就变成了一个抽象的工厂,它只是规划了一条条的生产线,让各个厂家的工厂来继承,通过这些生产线来生产自己想要的产品,哪怕这个公司没有这个产品,也可也拥有这个生产线,只是不使用而已。
为什么出现?
这里有个产品等级和产品族的概念,就是我的工厂已经不局限于只生产小米这个牌子了,我可以生产只要我生产线中有的所有厂家的机器,类似于一个超级工厂,只有有需要制造这些东西的厂家都能来找我。
看看代码:
//抽象工厂,接口
public interface IProductFactory {
IDrug createDrug();
IMachine createMachine();
IPackingBox createPackingBox();
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 16:57 2020-3-9
*/
public class bushenFactory implements IProductFactory {
@Override
public IDrug createDrug() {
return new BuShenDrug();
}
@Override
public IMachine createMachine() {
return new BuShenMachine();
}
@Override
public IPackingBox createPackingBox() {
return new BuShenPackingBox();
}
}
/**
* 接口,药,产品
*/
public interface IDrug {
public void introduce();
}
//机器,接口,产品
public interface IMachine {
public void introduce();
}
//包装盒,接口,产品
public interface IPackingBox {
public void introduce();
}
/**
* @Author Darker
* @Descrption 补肾药
* @Date : Created in 15:40 2020-3-9
*/
public class BuShenDrug implements IDrug{
@Override
public void introduce(){
System.out.println("我是补肾药了,兄弟肾该补补了");
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 16:58 2020-3-9
*/
public class BuShenMachine implements IMachine{
@Override
public void introduce() {
System.out.println("我是补肾仪");
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 17:00 2020-3-9
*/
public class BuShenPackingBox implements IPackingBox {
@Override
public void introduce() {
System.out.println("我是补肾系列产品的包装盒");
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 17:02 2020-3-9
*/
public class abstractFactoryTest {
public static void main(String[] args) {
bushenFactory bushenFactory = new bushenFactory();
IDrug drug = bushenFactory.createDrug();
drug.introduce();
IMachine machine = bushenFactory.createMachine();
machine.introduce();
IPackingBox packingBox = bushenFactory.createPackingBox();
packingBox.introduce();
}
}
接下来我们来看看抽象工厂模式的总结:
/**
* 使用场景:
* 1.不关注产品实例的创建和实现细节。
* 2.强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复代码
* 3.提供一个产品类的库,所有的产品以同样的接口出现,使客户端不依赖于具体的实现
* 优点
* 扩展性非常强
* 缺点
* 不符合开闭原则(慎用,但现实中用的也很多,代码迭代周期)
*/
这个抽象工厂模式就是类似于已经有个超级工厂可以有一条一系列产品的操作线了,举个例子,我们spring框架中的beanFactory里面,规划好了一些列对于bean的操作,它就像一个工厂,项目中的每一块都能来我这里生产一个一个的实体类工厂并且有对这些实体类基本操作的方法。
记得本人早些年的一个ssh项目中 ,每开启一个service,就会产生一个对应拥有基本增删改查的dao,它就是底层封装了一个curd的超级生产工厂,因为基本上的数据库连接需要做的事情就是curd,那么我们先建立一个dao的超类抽象工厂,规定了一系列curd操作。
然后用一个子类工厂来实现这个超级工厂,并重写curd方法。
最后通过你传进去的实体类就能拿到这个拥有curd的到了。
这就是一个抽象方法的运用,这个封装一下就不用每个对应的实体类的service都要写一遍curd,也就是大部分自动生成curd框架的核心思想。
当然,sping中的DefaultListableBeanFactory,里面也有一些列对于sping管理bean的操作,这里就不一一列举了。
好了,这就是java设计模式之工厂模式,是不是对你有一些帮助呢。