参考资料
图解设计模式
大话设计模式
设计模式之禅
github 我见过最好的设计模式
设计准则回顾
设计准则 | 解释 |
---|---|
开闭准则 | 对扩大凋谢,对批改敞开 |
依赖倒置准则 | 通过形象让哥哥模块互不影响,松耦合,面向接口编程 |
繁多职责准则 | 一个接口,类,办法只做一件事 |
接口隔离准则 | 保障纯洁性,不应该依赖于本人不须要的接口,有时候没方法能够通过适配器来解决 |
迪米特法令 | 起码晓得准则,一个类对其所依赖的类晓得的越少越好 |
里氏替换准则 | 子类能够扩大父类的性能然而不能扭转父类原有的性能 |
合成复用准则 | 尽量应用聚合和组合,少用继承 |
- 巧用 Json 工具类来做 json 转化
- JDBC 这一块是固定的,能够应用模板办法
工厂模式
简略工厂
案例
日历类
比如说日历类中获取对应日历的办法,通过传入参数来进行对应的对象的生成
日志
又或者说 logfactory 中的 log
public class CourseFactory {// public ICourse create(String name){// if("java".equals(name)){// return new JavaCourse();
// }else if("python".equals(name)){// return new PythonCourse();
// }else {
// return null;
// }
// }
// public ICourse create(String className){
// try {// if (!(null == className || "".equals(className))) {// return (ICourse) Class.forName(className).newInstance();
// }
//
// }catch (Exception e){// e.printStackTrace();
// }
// return null;
// }
// 泛型束缚
public ICourse create(Class<? extends ICourse> clazz){
try {if (null != clazz) {return clazz.newInstance();
}
}catch (Exception e){e.printStackTrace();
}
return null;
}
}
Mybatis SqlsesssionFactory 创立 Executor
org.apache.ibatis.session.Configuration#newExecutor(Transaction, ExecutorType)
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);
} else {executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
- 通过不同的 ExecurtorType 来创立 Executor
长处
只须要传入一个正确的参数,就能够获取咱们所须要的对象,无需晓得其创立的细节
毛病
工厂类的职责绝对过重,减少新的产品时须要批改工厂类的判断逻辑,不合乎开闭准则
问题
为什么肯定要用工厂模式,为什么工厂模式比间接
new Object()
好呢?
new Object 可能还得去配置和组装对象的很多额定信息
- 然而以后可能并不需要晓得这么多额定信息,且不心愿本人来组装,所以通过工厂来获取
- 例如用户只想要一辆车,可能并不想晓得车门的资料是什么厂商的,车轮是从哪里进口的
- 其次如果说工厂外面生产的货色产生一些小的变更,只须要间接在工厂的中央批改就行了,如果用户本人来 new 的,那么所有用户本人创立的中央都须要批改,其实次要还是
高内聚低耦合
工厂办法
定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂办法让类的实例化推延到子类中进行。
-
毛病
- 类的个数容易过多,减少了代码构造的复杂度
- 减少了零碎的抽象性和了解难度
例子
如果是简略以后可能创立不同的工厂产品可能须要组装各种属性,可能从 java 上来说就是须要 set 各种各样的货色,就会导致代码很长,而且一旦前期须要保护和变更其中一个工厂,可能会改和影响到其余工厂,所以这时候为了开闭准则,咱们能够把工厂形象进去,对应的工厂实现本人的工厂创立。
对于课程的例子
工厂形象
public interface ICourseFactory {ICourse create();
}
课程形象
public interface ICourse {
/**
* 录制视频
* @return
*/
void record();}
对于女娲造人的例子
形象办法中曾经不再须要传递相干参数了,因为每一个具体的工厂都曾经十分明确本人的职责:创立本人负责的产品类对象。
工厂办法 + 单例模式
单例模式代码
public class Singleton {
// 不容许通过 new 产生一个对象
private Singleton(){}
public void doSomething(){// 业务解决}
}
工厂办法 + 反射毁坏结构私有化来创立
咱们能够通过工厂来构建单例对象
public class SingletonFactory {
private static Singleton singleton;
static {
try {Class cl = Class.forName(Singleton.class.getName());
// 取得无参结构、java.lang.reflect.Constructor constructor =cl.getDeclaredConstructor();
// 设置无参结构是可拜访的
constructor.setAccessible(true);
// 产生一个实例对象
singleton = (Singleton)constructor.newInstance();} catch (Exception e) {// 异样解决}
}
public static Singleton getSingleton(){ return singleton;}
}
通过取得类结构器,而后设置拜访权限,生成一个对象,而后提供内部拜访,保障内存中的对象惟一。
当然,其余类也能够通过反射的形式建设一个单例对象,的确如此,
然而一个我的项目或团队是有章程和标准的,何况曾经提供了一个取得单例对象的办法,为什么还要从新创立一 个新对象呢?
除非是有人作恶。
Spring 中应用工厂办法 + 反射毁坏结构私有化
org.springframework.core.io.support.SpringFactoriesLoader#instantiateFactory
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
try {Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
if (!factoryClass.isAssignableFrom(instanceClass)) {
throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
}
Constructor<?> constructor = instanceClass.getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
return (T) constructor.newInstance();}
catch (Throwable ex) {throw new IllegalArgumentException("Unable to instantiate factory class:" + factoryClass.getName(), ex);
}
}
工厂办法 + 单例 提早初始化
public class ProductFactory {private static final Map<String,Product> prMap = new HashMap();
public static synchronized Product createProduct(String type) {
Product product =null;
// 如果 Map 中曾经有这个对象
if(prMap.containsKey(type)){product = prMap.get(type);
}else{if(type.equals("Product1")){product = new ConcreteProduct1();
}else{product = new ConcreteProduct2();
}
// 同时把对象放到缓存容器中
prMap.put(type,product);
} return product;
}
}
Dubbo 中 SPI 工厂办法 + 单例
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {if (type == null) {throw new IllegalArgumentException("Extension type == null");
}
if (!type.isInterface()) {throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
代码还比较简单,通过定义一个 Map 容器,包容所有产生的对象,如果在 Map 容器中曾经有的对象,则间接取出返回;如果没有,则依据 须要的类型产生一个对象并放入到 Map 容器中,以不便下次调用。
提早加载框架是能够扩大的,例如限度某一个产品类的最大实例化 数量,能够通过判断 Map 中已有的对象数量来实现,这样的解决是十分 有意义的,例如 JDBC 连贯数据库,都会要求设置一个 MaxConnections 最大连贯数量,该数量就是内存中最大实例化的数量。
提早加载还能够用在对象初始化比较复杂的状况下,例如硬件访 问,波及多方面的交互,则能够通过提早加载升高对象的产生和销毁带 来的复杂性。
创建对象须要大量反复的代码
客户端不依赖于产品类实例如何被创立,实现等细节。一个类通过其子类来指定创立哪个对象
最佳实际
工厂办法模式在我的项目中应用得十分频繁,以至于很多代码中都蕴含工厂办法模式。该模式简直享誉中外,但不是每个人都能用得好。游刃有余,熟练掌握该模式,多思考工厂办法如何利用,而且工厂办法模式还能够与其余模式混合应用(例如模板办法模式、单例模式、原型模式等),变动出无穷的优良设计,这也正是软件设计和开发的乐趣所在。
毛病
- 类的个数容易过多,减少复杂度
- 减少了零碎的抽象性和了解难度
问题
工厂办法它解决了简略工厂的哪个问题?
- 解决了不同工厂生成不同产品须要拆卸过多的代码在同一个类外面,违反了开闭准则,通过工厂办法来解决疾速扩大的问题
形象工厂
客户端无需理解其所调用工厂的具体类信息。
图解形象工厂
提供一个创立一系列相干或相互依赖对象的接口,毋庸指定他们具体的类
客户端不依赖于产品类实例如何被创立、实现等细节
强调一系列相干的产品对象(属于同一产品族)一起应用创建对象须要大量反复的代码
-
产品族
- 代表同一个品牌,不同产品
-
产品等级构造
- 同一产品不同品牌
例子
public class AbstractFactoryTest {public static void main(String[] args) {JavaCourseFactory factory = new JavaCourseFactory();
factory.createNote().edit();
factory.createVideo().record();
}
}
通用设计
咱们能够通过指定不同的形象工厂来创立对应的产品
实用场景
- 如果代码须要与多个不同系列的相干产品交互,然而因为无奈提前获取相干信息,或者出于对将来扩展性的思考,你不心愿代码基于产品的具体类进行构建,在这种状况下,你能够应用形象工厂。
- 形象工厂为你提供了一个接口,可用于创立每个系列产品的对象。只有代码通过该接口创建对象,那么你就不会生成与应用程序已生成的产品类型不统一的产品。
- 如果你有一个基于一组形象办法的类,且其次要性能因而变得不明确,那么在这种状况下能够思考应用形象工厂模式。
- 在设计良好的程序中,每个类仅负责一件事。如果一个类与多种类型产品交互,就能够思考将工厂办法抽取到独立的工厂类或具备残缺性能的形象工厂类中。
优缺点
长处 | 毛病 |
---|---|
你能够确保同一工厂生成的产品互相匹配。 | 因为采纳该模式须要向利用中引入泛滥接口和类,代码可能会比之前更加简单。 |
你能够防止客户端和具体产品代码的耦合。 | |
繁多职责准则。你能够将产品生成代码抽取到同一地位,使得代码易于保护。 | |
开闭准则。向应用程序中引入新产品变体时,你无需批改客户端代码。 |
-
不合乎开闭准则
- 如果在对应的工厂下面新增一个性能,所有实现的中央都须要去批改
慎用
- 如果在对应的工厂下面新增一个性能,所有实现的中央都须要去批改
- 不依赖于产品实例如何被创立,实现的细节
- 强调一系列相干的产品 (对立产品族) 一起应用穿件对象须要大量反复的的反复代码的场景
- 扩展性很强
问题
举例说出形象工厂的利用场景,我如果要减少一个答疑的产品应该怎么批改代码
- 比如说女娲造人,须要有男人和女人,也须要有白种人,黑种人,黄种人,那么咱们能够定义一个顶层接口
Humanfactory
而后FemaleFactory
和MaleFactory
都实现对应的HumanFactory
的接口,而后对应的工厂创立对应的产品- 减少答疑的产品的话须要新增一个答疑的接口,所有工厂都须要去批改补充对应的实现,所以改变会十分大
我的笔记仓库地址 gitee 快来给我点个 Star 吧