一.创建类设计模式
1.单例
单例优点:解决资源类的生成只生成一次,如id生成器。
缺:不是基于接口编程,后续有扩展时需要改代码。
替代:ioc容器单例,工厂模式(解决扩展问题)。
a.懒汉模式
在实际运行态调用的时候初始化。
需要将构造器私有,这样外部就无法通过构造器创建。
需要加锁。
伪代码:
public class CacheRepoFactory{
private volatile CacheRepo singleCacheRepo;
private CacheRepoFactory(){
}
public static CacheRepo getInstance(){
if(singeCacheRepo != null){
return singleCacheRepo;
}
syncronized(CacheRepoFactory.class){
if(singeCacheRepo != nulll){
return singleCacheRepo;
}
//创建实例
singleCacheRepo=new CacheRepo();
}
}
}
b.饥饿模式
如static对象,直接在jvm启动时加载初始化。
2.工厂
应用场景:对象创建有一定的复杂性,将这个复杂性封装在工厂里对使用方透明易用。
a.简单工厂
public class RuleConfigParserFactory {
private static final Map<String, RuleConfigParser> cachedParsers = new HashMap<>();
static {
cachedParsers.put("json", new JsonRuleConfigParser());
cachedParsers.put("xml", new XmlRuleConfigParser());
cachedParsers.put("yaml", new YamlRuleConfigParser());
cachedParsers.put("properties", new PropertiesRuleConfigParser());
}
public static IRuleConfigParser createParser(String configFormat) {
if (configFormat == null || configFormat.isEmpty()) {
return null;//返回null还是IllegalArgumentException全凭你自己说了算
}
IRuleConfigParser parser = cachedParsers.get(configFormat.toLowerCase());
return parser;
}
}
b.工厂方法(工厂的工厂)
当不同类型的实例化逻辑比较复杂可以将实例化逻辑抽到不同的工厂实例类里。
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;
}
}
3.建造者模式
与工厂模式差异:建造者解决更多定制化参数的实例构造问题。
工厂模式解决:通过一个实例类型指令给到工厂,工厂就能创建这个实例类型的实例。相对调用者的入参比较简单。
应用场景:当对象构造参数多,并且需要有些是必填,参数间可能还有依赖关系,如资源池的minThread及maxThread就有一定关。此时希望在构建对象的时候就能提示错误。
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;
}
//...省略getter方法...
//我们将Builder类设计成了ResourcePoolConfig的内部类。
//我们也可以将Builder类设计成独立的非内部类ResourcePoolConfigBuilder。
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 (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("...");
}
if (maxIdle > maxTotal) {
throw new IllegalArgumentException("...");
}
if (minIdle > maxTotal || minIdle > maxIdle) {
throw new IllegalArgumentException("...");
}
return new ResourcePoolConfig(this);
}
public Builder setName(String name) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("...");
}
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;
}
}
}
// 这段代码会抛出IllegalArgumentException,因为minIdle>maxIdle
ResourcePoolConfig config = new ResourcePoolConfig.Builder()
.setName("dbconnectionpool")
.setMaxTotal(16)
.setMaxIdle(10)
.setMinIdle(12)
.build();
二.结构型
1.代理模式
通过调用代理类对原有类做非业务性的增强,如日志,缓存aop处理。
如doubbo就是用代理,调用远程服务就好像本地服务一样。
2.装饰者模式
角色:业务组件,具体业务组件实现,装饰者基类,装饰者实现。
通过在装饰者实现中组合业务组件实现,达到对原业务组件的功能增强。
从逻辑上装饰者可以组合另一个装饰者,但最终要有一个业务组件实现。
核心组件图:
java示例:
3.适配器
对外部希望展示一个更加合理的接口,但使用的能力是以前的旧功能组件。
4.门面模式
应用场景:让使用者更易用。
比如解决app性能的场景,原先一个页面区域需要调用3次rpc(串形化的),现在用门面封装一次请求搞定。
从架构上看,门面模式有点类似与app或pc交互的一层,比如手机端和pc需要的接口不同,返回结果也有差异(性能不同),所以这种情况做不同端的门面接口是合理的。
三.行为型
1.观察者
应用场景:通知者通知一个事情,观察者去做订阅处理。
2.策略模式
应用场景:去除多if else代码,通过策略模式去做扩展。
如底层支付渠道处理器可以设计成策略模式,入参是支付渠道产品code,如支付宝扫码,然后可以获取到对应的策略处理类。
3.职责链
请求经过多个职责处理器。
如metaq对于消息过滤需要根据tag以及property进行过滤。
servlet filter如下:
4.迭代器模式
应用场景:迭代容器,将迭代的逻辑横向抽象出来,使这部分逻辑更内聚。