设计模式的由来
设计模式起源于软件工程领域,主要是为了解决软件开发过程中常见设计问题而提出的一套解决方案。
该名词最早是在1994年由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides在《设计模式:可复用面向对象软件的基础》一书中提出的,大家通常都称这几位作者为 Gang of Four,简称GOF
为什么要学习设计模式
设计模式是内功
当程序员到达一定阶段后,功能的实现已经不是阻碍后,应该关注功能如何较优的设计,代码如何实现,是否高内聚低耦合,是否有良好的扩展性、鲁棒性等等
而设计模式则可以很好得帮助你,且到你理解了设计模式,在看很多源码时,将事半功倍!
如何学习设计模式
设计模式是一种思想,一类问题的解决方案描述,而不是具体的代码实现或者算法
因此学习设计模式应该最先理解设计模式的意图,然后带着意图进行学习理解
创建型设计模式
创建型设计模式主要关注的是如何有效地创建对象,以便在不同情境下满足灵活性和复用性。
单例
作用:使用同一个实例,避免了多个对象所消耗的资源,同时也减少了创建对象时消耗的性能
懒汉不安全
懒汉是指在第一次调用的时候才创建实例
但是由于Java的线程安全机制,当多个线程同时调用getInstance()方法时,可能会创建多个实例,所以这种模式是不安全的。
public class Singleton {
private static Singleton instance;
public static Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
懒汉安全
这种方式虽然保证了线程安全,但是锁的粒度太大了
多线程获取实例时,每个线程都要获取锁,缺点效率太低
public class Singleton2 {
private static Singleton2 singleton2;
public static synchronized Singleton2 getInstance(){
if (singleton2 == null){
singleton2 = new Singleton2();
}
return singleton2;
}
}
懒汉安全(volatile + 双重校验)
当前线程获取到锁,其他线程阻塞在if判断上,如果当前线程释放锁后,其他线程获取到锁,会再次创建对象,线程安全,且性能高
public class Singleton3 {
private static volatile Singleton3 singleton3;
public static Singleton3 getInstance(){
if(singleton3 == null){
synchronized (Singleton.class){
if (singleton3 == null){
singleton3 = new Singleton3();
}
}
}
return singleton3;
}
}
饿汉式
public class Singleton4 {
/**
* 饿汉式
*/
private static Singleton4 instance = new Singleton4();
public static Singleton4 getInstance(){
return instance;
}
}
饿汉式,会提前创建好对象,所以不存在线程安全问题
工厂模式
作用:通过工厂可以直接获取不同“产品”,封装了“产品”的具体实现,让使用者再使用时无需关心对象具体实现,更加易用
Spring中就是用到了工厂模式,如BeanFactory
public interface IRuleConfigParserFactory {
IRuleConfigParser createParser();
}
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new JsonRuleConfigParser();
}
}
public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new XmlRuleConfigParser();
}
}
public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new YamlRuleConfigParser();
}
}
public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new PropertiesRuleConfigParser();
}
}
public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) {
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParserFactory parserFactory = RuleConfigParserFactoryMap.getParserFactory(ruleConfigFileExtension);
if (parserFactory == null) {
throw new InvalidRuleConfigException("Rule config file format is not supported: " + ruleConfigFilePath);
}
IRuleConfigParser parser = parserFactory.createParser();
String configText = "";
//从ruleConfigFilePath文件中读取配置文本到configText中
RuleConfig ruleConfig = parser.parse(configText);
return ruleConfig;
}
private String getFileExtension(String filePath) {
//...解析文件名获取扩展名,比如rule.json,返回json
return "json";
}
}
//因为工厂类只包含方法,不包含成员变量,完全可以复用,
//不需要每次都创建新的工厂类对象,所以,简单工厂模式的第二种实现思路更加合适。
public class RuleConfigParserFactoryMap { //工厂的工厂
private static final Map<String, IRuleConfigParserFactory> cachedFactories = new HashMap<>();
static {
cachedFactories.put("json", new JsonRuleConfigParserFactory());
cachedFactories.put("xml", new XmlRuleConfigParserFactory());
cachedFactories.put("yaml", new YamlRuleConfigParserFactory());
cachedFactories.put("properties", new PropertiesRuleConfigParserFactory());
}
public static IRuleConfigParserFactory getParserFactory(String type) {
if (type == null || type.isEmpty()) {
return null;
}
IRuleConfigParserFactory parserFactory = cachedFactories.get(type.toLowerCase());
return parserFactory;
}
}
抽象工厂模式
举例:
工厂模式是指比如我有一批白、红葡萄原料,我有一座工厂,工厂可以基于葡萄生产红葡萄酒、白葡萄酒。对于使用者来讲不需要知道酒是如何实现或制造的,只要是提供葡萄原料(参数),就能得到产品(对象),使用者不用关心具体实现,将制造酒逻辑内聚。
抽象工厂是指,在工厂上再加一层抽象,我有很多工厂,原先我的工厂有生产酒、还有生产豆腐的,抽象工厂模式,则对工厂模式制定标准
解释一下为啥叫抽象工厂,抽象是指在指定能生产哪些类产品时,通常使用的是抽象类,因为抽象类包含抽象方法和默认方法,可以初始化一些默认逻辑
比如我现在宣布我的工厂只能生产葡萄酒和葡萄干,你们这些工厂自己去实现,同时我还告诉他们我有葡萄清洗的机器(默认方法),你们自己看情况去使用;
最后我旗下有的工厂可以生产葡萄干,有的工厂可以生产葡萄酒,有的工厂都可以生产。
原料来了,先找到我的工厂,工厂再生产产品。
抽象工厂模式 相当于在抽象工厂模式基础上又进行一层抽象,工厂模式是对产品的抽象封装,而工厂模式则是对工厂的再一层抽象,
详细实现请参考文末github链接
建造者模式
作用:更简易的创建对象、将复杂的创建逻辑与表示分离、增加可读性、可维护性
建造者基本结构
public class Resource{
private String name;
public Resource(Builder builder){
this.name = builder.name;
}
public static class Builder{
private String name;
public Resource builder(){
return new Resource(this);
}
public Builder setName(String name){
// 校验
this.name = name;
return this;
}
}
}
以上是一个基本结构,下面这个例子是常用场景中的真实使用
/**
* 构建者模式
* 通过内部类为父类对象赋值
*/
public class ResourcePoolConfig {
private String name;
private int maxTotal;
private int maxIdle;
private int minIdle;
private ResourcePoolConfig(Builder builder) {
this.name = builder.name;
this.maxTotal = builder.maxTotal;
this.maxIdle = builder.maxIdle;
this.minIdle = builder.minIdle;
}
public static class Builder {
private static final int DEFAULT_MAX_TOTAL = 8;
private static final int DEFAULT_MAX_IDLE = 8;
private static final int DEFAULT_MIN_IDLE = 0;
private String name;
private int maxTotal = DEFAULT_MAX_TOTAL;
private int maxIdle = DEFAULT_MAX_IDLE;
private int minIdle = DEFAULT_MIN_IDLE;
public ResourcePoolConfig build() {
// 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等
if (maxIdle > maxTotal) {
throw new IllegalArgumentException("...");
}
if (minIdle > maxTotal || minIdle > maxIdle) {
throw new IllegalArgumentException("...");
}
return new ResourcePoolConfig(this);
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setMaxTotal(int maxTotal) {
if (maxTotal <= 0) {
throw new IllegalArgumentException("...");
}
this.maxTotal = maxTotal;
return this;
}
public Builder setMaxIdle(int maxIdle) {
if (maxIdle < 0) {
throw new IllegalArgumentException("...");
}
this.maxIdle = maxIdle;
return this;
}
public Builder setMinIdle(int minIdle) {
if (minIdle < 0) {
throw new IllegalArgumentException("...");
}
this.minIdle = minIdle;
return this;
}
}
}
使用
ResourcePoolConfig config = new ResourcePoolConfig.Builder()
.setName("db")
.setMaxTotal(10)
.setMaxIdle(5)
.setMinIdle(2)
.build();
具体实现参考文档
https://github.com/jiutaotan/design-pattern