翻译与github 上 java design 项目,旨在提供java设计模式的速查。
引用
[3]java 设计模式
标题 | 类别 | 难度 |
---|---|---|
abstract-document(抽象文档) | 结构型 | 中等 |
灵活的实现类型安全,与语言无关。
引用[1]
所谓抽象就是:概念的聚合(Aggregate),可以很安全地忽略细节去使用一个概念,在不同的层次处理不同的细节。现实世界中很多东西已经是某种程度上的抽象了,举个例子:当你开门时,你直接去操纵门把手就行了,你不会去操心门具体用的什么木质材料、甚至分子层面,否则每天你开门时都将寸步难行。当你没能正确地抽象时,你的系统就像一扇过度复杂而无法打开的门,而好的开发者则会从门把手、门、房子等(对应软件开发中的方法、类、包等层次)各个层面创造出正确的抽象来降低复杂度。
Java 抽象文档设计模式例子分析
标题 | 类别 | 难度 |
---|---|---|
abstract-factory(抽象文档) | 创建型 | 中等 |
提供一个接口,用于创建相关或从属的一类对象,而不指定它们的具体类。
真实世界的例子
创建一个王国,我们需要对象的共同主题。精灵王国需要精灵王,精灵城堡和精灵的军队而兽人王国需要一个兽人王,兽人和兽人军队的城堡。在王国中对象间的依赖
字面意思
工厂的工厂;把个体和相关的、依赖的工厂组合在一起而不指定具体的工厂。
维基百科
抽象工厂模式提供了一种方法来封装一组具有共同主题的工厂,而不指定具体的类。
编程例子
翻译上面的王国例子。首先,我们为王国中的对象提供了一些接口和实现。
public interface Castle {
String getDescription();
}
public interface King {
String getDescription();
}
public interface Army {
String getDescription();
}
// Elven implementations ->
public class ElfCastle implements Castle {
static final String DESCRIPTION = "This is the Elven castle!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ElfKing implements King {
static final String DESCRIPTION = "This is the Elven king!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ElfArmy implements Army {
static final String DESCRIPTION = "This is the Elven Army!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
// 兽人实现类似
然后我们王国工厂的抽象和实现
public interface KingdomFactory {
Castle createCastle();
King createKing();
Army createArmy();
}
public class ElfKingdomFactory implements KingdomFactory {
public Castle createCastle() {
return new ElfCastle();
}
public King createKing() {
return new ElfKing();
}
public Army createArmy() {
return new ElfArmy();
}
}
public class OrcKingdomFactory implements KingdomFactory {
public Castle createCastle() {
return new OrcCastle();
}
public King createKing() {
return new OrcKing();
}
public Army createArmy() {
return new OrcArmy();
}
}
现在我们有了一个抽象工厂,让我们制造相关的家族,即精灵王国工厂制造精灵城堡、国王和军队等。
KingdomFactory factory = new ElfKingdomFactory();
Castle castle = factory.createCastle();
King king = factory.createKing();
Army army = factory.createArmy();
castle.getDescription(); // Output: This is the Elven castle!
king.getDescription(); // Output: This is the Elven king!
army.getDescription(); // Output: This is the Elven Army!
标题 | 类别 | 难度 |
---|---|---|
adapter(适配器) | 结构型 | GOF,中等 |
将一个类的接口转换成客户端期待的接口。适配器让彼此之间接口不兼容的类工作。
真实例子
你想把内存卡中的图片传输到自己的电脑上. 为了传输它们,你需要一种与你的计算机端口兼容的适配器,这样你就可以把内存卡附加到你的计算机上。在这种情况下,读卡器是一个适配器。
另一个例子是著名的电源适配器;一个三条腿的插头不能连接到一个两个孔的插座,它需要使用电源适配器,使两个孔的插座兼容。
另一个例子是译者将一个人的话翻译给另一个人。
字面意思
适配器模式允许您在适配器中包装一个不兼容的对象,使其与另一个类兼容。
维基描述
在软件工程中,适配器模式是一种软件设计模式,它允许现有类的接口作为另一个接口使用。它通常用于使现有类与其他类协同工作而不修改源代码。
编程例子
考虑一个只能使用划艇的船长,根本不能使用帆船。
首先我们有接口划艇
和帆船
public interface RowingBoat {
void row();
}
public class FishingBoat {
private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);
public void sail() {
LOGGER.info("The fishing boat is sailing");
}
}
并且船长希望实现“RowingBoat”接口能够行驶船
public class Captain implements RowingBoat {
private RowingBoat rowingBoat;
public Captain(RowingBoat rowingBoat) {
this.rowingBoat = rowingBoat;
}
@Override
public void row() {
rowingBoat.row();
}
}
现在让我们说海盗来了,船长需要逃跑,但只有渔船可用。我们需要创建一个适配器,让船长用他的划船技巧操作渔船。
public class FishingBoatAdapter implements RowingBoat {
private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoatAdapter.class);
private FishingBoat boat;
public FishingBoatAdapter() {
boat = new FishingBoat();
}
@Override
public void row() {
boat.sail();
}
}
现在船长
可以用渔船
逃离海盗。
Captain captain = new Captain(new FishingBoatAdapter());
captain.row();
博主思考
比如说一个厨师,他拥有各种各样的专业知识,比如烤面包,但是他并不需要知道如何从原材料中获取食材,他只需要知道如何使用食材。适配器很多时候就像提供资源一样,提供面粉,
牛奶,鸡蛋,黄油,酱油…也就是适配器跟原材料到可使用的食材打交道,生成厨师需要的食材。好处说白了就是,厨师(领域知识)只需要专精他如何制作更美味更好的食物。而我们(客户端程序员)只需要提供厨师需要的资源就行了 :)
类和对象适配器有不同的取舍。一个类适配器
对象适配器
标题 | 类别 | 难度 |
---|---|---|
Aggregator Microservices(聚合器-微服务) | 结构型 | Java,Spring |
用户对Aggregator进行单次调用,然后聚合器调用每个相关的微服务并进行收集
数据,将业务逻辑应用于它,并进一步发布为REST端点。
聚合器的更多变体:
- 代理微服务设计模式:根据业务需求调用不同的微服务。
- 链接的微服务设计模式:在这种情况下,每个微服务依赖/链接到一系列
的其他微服务。
当您需要统一的API时,无论客户端设备有多少种,使用Aggregator Microservices模式。
标题 | 类别 | 难度 |
---|---|---|
api-网关 | 结构型 | Java,Spring,中等难度 |
聚集微服务调用在一个位置: API 网关. 用户只调用api网关, Api网关调用其他微服务.
当使用API网关模式时
标题 | 类别 | 难度 |
---|---|---|
异步方法调用 | 并发 | Java,中等难度,函数式,响应式 |
异步方法调用设计模式的作用是等待任务结果时不被阻塞。这种设模式对多个独立的任务提供并行处理,并且提供回调(callback)或等待阻塞当前线程(await)拿到结果值。
如下情况使用异步调用模式:
标题 | 类别 | 难度 |
---|---|---|
阻塞模式(Balking Pattern) | 并发 | Java,中等难度 |
阻塞模式被用于阻止某个对象执行某些不完全或不适当状态的代码
如下情况使用阻塞模式:
标题 | 类别 | 难度 |
---|---|---|
桥接模式(Bridge Pattern) | 结构型 | Java,GOF,中等难度 |
将抽象(abstraction)与它的实现(implementation)解耦,使两者可以独立变化。
真实例子
考虑你有一个具有魔法的武器,你应该允许不同的武器拥有不同的魔法。你会怎么做? 创建多个武器副本赋予每个武器副本魔法或者创建单独魔法类并将其设置到武器类,桥接模式允许你实现第二种方式
字面意义
桥接模式更喜欢组合相对于继承. 实现细节从一个层次结构转移到另一个具有单独层次结构的对象
维基百科
桥接模式是一种用于软件工程的设计模式,其目的是“将抽象与实现分离,从而使两者独立变化”。
编程例子
从上面翻译我们的武器示例。这里我们有Weapon
层
public interface Weapon {
void wield();
void swing();
void unwield();
Enchantment getEnchantment();
}
public class Sword implements Weapon {
private final Enchantment enchantment;
public Sword(Enchantment enchantment) {
this.enchantment = enchantment;
}
@Override
public void wield() {
LOGGER.info("The sword is wielded.");
enchantment.onActivate();
}
@Override
public void swing() {
LOGGER.info("The sword is swinged.");
enchantment.apply();
}
@Override
public void unwield() {
LOGGER.info("The sword is unwielded.");
enchantment.onDeactivate();
}
@Override
public Enchantment getEnchantment() {
return enchantment;
}
}
public class Hammer implements Weapon {
private final Enchantment enchantment;
public Hammer(Enchantment enchantment) {
this.enchantment = enchantment;
}
@Override
public void wield() {
LOGGER.info("The hammer is wielded.");
enchantment.onActivate();
}
@Override
public void swing() {
LOGGER.info("The hammer is swinged.");
enchantment.apply();
}
@Override
public void unwield() {
LOGGER.info("The hammer is unwielded.");
enchantment.onDeactivate();
}
@Override
public Enchantment getEnchantment() {
return enchantment;
}
}
分离的施法层
public interface Enchantment {
void onActivate();
void apply();
void onDeactivate();
}
public class FlyingEnchantment implements Enchantment {
@Override
public void onActivate() {
LOGGER.info("The item begins to glow faintly.");
}
@Override
public void apply() {
LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
}
@Override
public void onDeactivate() {
LOGGER.info("The item's glow fades.");
}
}
public class SoulEatingEnchantment implements Enchantment {
@Override
public void onActivate() {
LOGGER.info("The item spreads bloodlust.");
}
@Override
public void apply() {
LOGGER.info("The item eats the soul of enemies.");
}
@Override
public void onDeactivate() {
LOGGER.info("Bloodlust slowly disappears.");
}
}
action
在两个层次之间
Sword enchantedSword = new Sword(new SoulEatingEnchantment());
enchantedSword.wield();
enchantedSword.swing();
enchantedSword.unwield();
// 剑被拿起.
// 开始杀戮.
// 剑被使用.
// 这个魔法吞噬敌人的灵魂。.
// 剑被放下.
// 嗜血慢慢消失.
Hammer hammer = new Hammer(new FlyingEnchantment());
hammer.wield();
hammer.swing();
hammer.unwield();
使用Bridge模式
标题 | 类别 | 难度 |
---|---|---|
构建模式(Builder Pattern) | 构建型 | Java,GOF,中等难度 |
将复杂对象的构造与它的表现分离,使相同的构建过程可以创建不同的表现。
真实例子
想象一个角色扮演游戏的角色生成器。最简单的选择是让计算机为你创建角色。但是,如果你想选择诸如职业,性别,头发颜色等字符的细节,角色生成器成为一个循序渐进的过程,完成时,所有的选择都准备好了.
字面意思
允许您创建不同风格的对象,同时避免构造器污染。 当有可能创建几种不同的对象时很有用。 或者当创建对象时涉及很多步骤。
维基百科
构建器模式是一种对象创建软件设计模式,其目的是找到一种方案,解决伸缩构造函数反模式。
话虽如此,我还是要补充一点,关于伸缩构造函数反模式是什么。在某一点上,我们都看到了如下构造函数:
public Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) {
}
您可以看到,构造函数的数量可能会很快失控,并且可能难以理解参数的排列。 如果您以后要添加更多选项,此参数列表可能会不断增长。 这被称为伸缩构造器反模式。
编程例子
理想的选择是使用Builder模式。 首先我们有我们要创造的英雄
public final class Hero {
private final Profession profession;
private final String name;
private final HairType hairType;
private final HairColor hairColor;
private final Armor armor;
private final Weapon weapon;
private Hero(Builder builder) {
this.profession = builder.profession;
this.name = builder.name;
this.hairColor = builder.hairColor;
this.hairType = builder.hairType;
this.weapon = builder.weapon;
this.armor = builder.armor;
}
}
And then we have the builder
public static class Builder {
private final Profession profession;
private final String name;
private HairType hairType;
private HairColor hairColor;
private Armor armor;
private Weapon weapon;
public Builder(Profession profession, String name) {
if (profession == null || name == null) {
throw new IllegalArgumentException("profession and name can not be null");
}
this.profession = profession;
this.name = name;
}
public Builder withHairType(HairType hairType) {
this.hairType = hairType;
return this;
}
public Builder withHairColor(HairColor hairColor) {
this.hairColor = hairColor;
return this;
}
public Builder withArmor(Armor armor) {
this.armor = armor;
return this;
}
public Builder withWeapon(Weapon weapon) {
this.weapon = weapon;
return this;
}
public Hero build() {
return new Hero(this);
}
}
然后它可以用作:
Hero mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER).build();
使用Builder模式
标题 | 类别 | 难度 |
---|---|---|
业务委托(Business Delegate) | 业务层 | Java,中等难度 |
业务委托模式在表示层和业务层之间添加一个抽象层。 通过使用这种设计模式,
我们获得了层之间的松散耦合,
介于层次之间并封装有关如何定位,连接到,
并与构成应用程序的业务对象进行交互。
何时使用业务委托模式
标题 | 类别 | 难度 |
---|---|---|
缓存模式(caching design) | 其他 | Java,中等难度,性能相关 |
通过不立即释放资源的方式避免昂贵的资源再获取操作。 资源保留他们的身份,保留在一些快速访问存储,并重新使用,以避免再次获取它们。
何时使用缓存模式
标题 | 类别 | 难度 |
---|---|---|
回调模式(callback) | 其他 | Java,简单难度,函数式编程 |
回调是作为一段可传递的执行代码,是其他代码的参数。在合适的时间点执行。
何时使用回调模式时
标题 | 类别 | 难度 |
---|---|---|
责任链模式(chain design) | 行为型 | Java,GOF,中等难度 |
通过给与一个或更多对象处理请求的机会避免将请求的发送者与接收者耦合。责任链接收
对象,并沿着链传递请求,直到有对象处理它为止。
真实例子
兽王向他的军队发出明确的命令。 最接近的人是指挥官,然后是军官,然后是士兵。 这里的指挥官,军官和士兵组成了一连串的责任。
字面意思
它有助于建立一个对象链。 请求从一端进入,并保持前进从对象到对象,直到找到合适的处理程序。
维基描述
在面向对象设计中,责任链模式是由命令对象源和一系列处理对象组成的设计模式。 每个处理对象包含定义可以处理的命令对象的类型的逻辑; 其余的传递给链中的下一个处理对象。
编程例子
用上面的兽人转换成我们的例子。 首先我们有Request
类
public class Request {
private final RequestType requestType;
private final String requestDescription;
private boolean handled;
public Request(final RequestType requestType, final String requestDescription) {
this.requestType = Objects.requireNonNull(requestType);
this.requestDescription = Objects.requireNonNull(requestDescription);
}
public String getRequestDescription() { return requestDescription; }
public RequestType getRequestType() { return requestType; }
public void markHandled() { this.handled = true; }
public boolean isHandled() { return this.handled; }
@Override
public String toString() { return getRequestDescription(); }
}
public enum RequestType {
DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
}
然后RequestHandler
的层次结构
public abstract class RequestHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class);
private RequestHandler next;
public RequestHandler(RequestHandler next) {
this.next = next;
}
public void handleRequest(Request req) {
if (next != null) {
next.handleRequest(req);
}
}
protected void printHandling(Request req) {
LOGGER.info("{} handling request \"{}\"", this, req);
}
@Override
public abstract String toString();
}
public class OrcCommander extends RequestHandler {
public OrcCommander(RequestHandler handler) {
super(handler);
}
@Override
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) {
printHandling(req);
req.markHandled();
} else {
super.handleRequest(req);
}
}
@Override
public String toString() {
return "兽人指挥官";
}
}
// OrcOfficer and OrcSoldier are defined similarly as OrcCommander
然后我们有兽人国王,他命令并形成了链
public class OrcKing {
RequestHandler chain;
public OrcKing() {
buildChain();
}
private void buildChain() {
chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null)));
}
public void makeRequest(Request req) {
chain.handleRequest(req);
}
}
然后使用如下
OrcKing king = new OrcKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); // Orc commander handling request "defend castle"
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); // Orc officer handling request "torture prisoner"
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); // Orc soldier handling request "collect tax"
何时使用责任链
标题 | 类别 | 难度 |
---|---|---|
命令模式(command design) | 行为型 | Java,GOF,中等难度,函数式编程 |
将请求封装为一个对象,从而使你用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及
支持可撤销的操作。
当你想要使用命令模式
标题 | 类别 | 难度 |
---|---|---|
组合模式(Composite design) | 结构型 | Java,GOF,中等难度 |
将对象组合成树结构以表示部分整体层次结构。 组合允许客户端处理单个对象和组合的对象一致。
真实例子
每个句子都是由字符组成的单词组成。 这些对象中的每一个都是可打印的,它们可以在它们之前或之后打印出一些东西,因为句子总是以完全停止的方式结束,而且在它之前总是有空格
字面意思
组合模式使客户能够以统一的方式对待各个对象。
维基百科
在软件工程中,组合模式是分区设计模式。 组合模式描述了一组对象的处理方式与对象的单个实例相同。 组合的意图是将对象“组合”成树结构以表示部分整体层次结构。 实现组合模式使客户能够一致地处理单个对象和对象组。
编程例子
以我们上面的例子为例。在这里,我们有不同类型的基类,可打印的字符
public abstract class LetterComposite {
private List children = new ArrayList<>();
public void add(LetterComposite letter) {
children.add(letter);
}
public int count() {
return children.size();
}
protected void printThisBefore() {}
protected void printThisAfter() {}
public void print() {
printThisBefore();
for (LetterComposite letter : children) {
letter.print();
}
printThisAfter();
}
}
public class Letter extends LetterComposite {
private char c;
public Letter(char c) {
this.c = c;
}
@Override
protected void printThisBefore() {
System.out.print(c);
}
}
public class Word extends LetterComposite {
public Word(List letters) {
for (Letter l : letters) {
this.add(l);
}
}
@Override
protected void printThisBefore() {
System.out.print(" ");
}
}
public class Sentence extends LetterComposite {
public Sentence(List words) {
for (Word w : words) {
this.add(w);
}
}
@Override
protected void printThisAfter() {
System.out.print(".");
}
}
Then we have a messenger to carry messages
public class Messenger {
LetterComposite messageFromOrcs() {
List words = new ArrayList<>();
words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'), new Letter('e'), new Letter('r'), new Letter('e'))));
words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter('r'), new Letter('e'))));
words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
words.add(new Word(Arrays.asList(new Letter('a'))));
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'), new Letter('i'), new Letter('p'))));
words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter('r'), new Letter('e'))));
words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s'))));
words.add(new Word(Arrays.asList(new Letter('a'))));
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'), new Letter('y'))));
return new Sentence(words);
}
LetterComposite messageFromElves() {
List words = new ArrayList<>();
words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'), new Letter('c'), new Letter('h'))));
words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'), new Letter('n'), new Letter('d'))));
words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'), new Letter('u'), new Letter('r'), new Letter('s'))));
words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'), new Letter('o'), new Letter('m'))));
words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'), new Letter('u'), new Letter('r'))));
words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'), new Letter('u'), new Letter('t'), new Letter('h'))));
return new Sentence(words);
}
}
然后它可以用作
LetterComposite orcMessage = new Messenger().messageFromOrcs();
orcMessage.print(); // Where there is a whip there is a way.
LetterComposite elfMessage = new Messenger().messageFromElves();
elfMessage.print(); // Much wind pours from your mouth.
使用组合模式时
标题 | 类别 | 难度 |
---|---|---|
转换器(convert) | 简单难度 |
转换器模式的目的是提供一种通用的,在相应类型之间进行双向转换,类型转换不需要了解彼此,是一种干净的转换方式。 此外,转换器模式引入了双向收集映射,将样板代码减少到最小。
在以下情况下使用转换器模式:
标题 | 类别 | 难度 |
---|---|---|
命令查询的责任分离(CQRS design) | 结构型 | 中等难度 |
CQRS命令查询责任分离 - 将查询与命令端分开(博主理解:有点像数据库的读写分离,但对写一端有了历史记录变化的追溯,) 。
使用CQRS模式
标题 | 类别 | 难度 |
---|---|---|
数据访问对象模式(dao design) | 持久层 | 初始难度 |
在以下任何情况下使用数据访问对象
标题 | 类别 | 难度 |
---|---|---|
.数据总线模式(Data Bus design) | 结构型 | 中等难度 |
在不需要应用组件彼此了解的情况下,发送消息/事件。 他们只需要知道关于要发送的消息/事件的类型。
使用数据总线模式
与数据总线相似
标题 | 类别 | 难度 |
---|---|---|
数据映射模式(data-mapper design) | 结构型 | 简单难度 |
用于在对象和数据库之间移动数据,同时保持它们彼此独立。
在以下任何情况下使用数据映射器
标题 | 类别 | 难度 |
---|---|---|
数据传输对象模式(data-transfer-object design) | 结构型 | 简单难度 |
从客户端到服务器一次性传递具有多个属性的数据,
以避免多次呼叫到远程服务器。
使用数据传输对象模式
标题 | 类别 | 难度 |
---|---|---|
装饰模式(Decorator design) | 结构型 | 简单难度 |
Wrapper
给对象动态附加额外的责任,装饰模式为扩展子类提供了灵活的替代方案。
真实世界
生活在附近山丘上的一个愤怒的巨魔。 平时它是空手的,但有时它有武器。 为了武装巨魔,没有必要创建一个新的巨魔,而是用合适的武器动态地装饰它。
字面意思
装饰模式使您可以在运行时动态地更改对象的行为,将其包装在装饰器类的对象中。
维基解释
在面向对象编程中,装饰器模式是一种设计模式,允许静态或动态地将行为添加到单个对象,而不会影响同一类中其他对象的行为。 装饰师模式对于遵守单一责任原则通常是有用的,因为它允许在具有独特关注领域的类别之间划分功能。
编程例子
让我们看看巨魔的例子。 首先我们有一个简单的巨魔实现巨魔接口
public interface Troll {
void attack();
int getAttackPower();
void fleeBattle();
}
public class SimpleTroll implements Troll {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTroll.class);
@Override
public void attack() {
LOGGER.info("The troll tries to grab you!");
}
@Override
public int getAttackPower() {
return 10;
}
@Override
public void fleeBattle() {
LOGGER.info("The troll shrieks in horror and runs away!");
}
}
接下来我们要为巨魔添加棒子。 我们可以通过使用装饰器来动态地执行
public class ClubbedTroll implements Troll {
private static final Logger LOGGER = LoggerFactory.getLogger(ClubbedTroll.class);
private Troll decorated;
public ClubbedTroll(Troll decorated) {
this.decorated = decorated;
}
@Override
public void attack() {
decorated.attack();
LOGGER.info("The troll swings at you with a club!");
}
@Override
public int getAttackPower() {
return decorated.getAttackPower() + 10;
}
@Override
public void fleeBattle() {
decorated.fleeBattle();
}
}
这是巨魔的行为
// simple troll
Troll troll = new SimpleTroll();
troll.attack(); //巨魔试图抓住你!
troll.fleeBattle(); // 巨魔逃跑了!
//通过添加装饰器来改变简单巨魔的行为
troll = new ClubbedTroll(troll);
troll.attack(); //巨魔试图抓住你! 巨魔用棍子敲你头!
troll.fleeBattle(); //巨魔逃跑了!
使用装饰模式如下
标题 | 类别 | 难度 |
---|---|---|
委托模式(delegation design) | 行为型 | 简单难度 |
代理模式(Proxy Pattern)
这是一种技术,其中一个对象表现某种行为给外部,但是该行为的实现委托给相关联的对象。(博主描述,比如你一个对象要调用一个方法,但是你不直接操作这个对象本身,而是操作和他关联的一个其他对象,并且该对象实现了那个方法的接口)
为了实现以下目的,使用委托模式
标题 | 类别 | 难度 |
---|---|---|
依赖注入模式(Dependency Injection) | 行为型 | 简单难度 |
依赖注入是一种软件设计模式,其中一个或更多的依赖(或服务)被注入或通过引用传递到依赖对象(或客户端)并作为客户端状态的一部分。该模式将客户机的依赖关系的创建与其自身的分离行为,允许程序设计松散耦合并遵循
控制反转和单一责任原则。
何时使用依赖注入模式
标题 | 类别 | 难度 |
---|---|---|
双重锁模式(Double Checked Locking design) | 并发型 | 简单难度,原子性 |
通过先判断是否需要获取锁来减少锁获取的开销,如果需要执行锁,在进行实际的锁操作。在锁定之后再判断一次需要获取锁,若不需要择表明不需要执行逻辑,加快锁的释放。
何时使用双重检查锁定模式
标题 | 类别 | 难度 |
---|---|---|
双重派对模式(Double Dispatch design) | 其他 | 中等难度,原子性 |
双重分发模式创建基于接收器的可维护的动态行为和参数类型。
何时使用双重派对模式
标题 | 类别 | 难度 |
---|---|---|
执行程序流监测器(EIP WireTap design) | 企业集成 | 中等难度 |
在大多数集成案例中,需要监控流经系统的消息。 通常实现通过拦截消息并将其重定向到不同的位置,如控制台,文件系统或数据库。重要的是,这种功能不应该修改原始消息并影响处理路径。
使用执行程序流监测器
标题 | 类别 | 难度 |
---|---|---|
事件聚合器(event-aggregator design) | 结构型 | 简单难度,响应式 |
一个具有很多对象的系统会导致复杂性,客户端希望订阅活动。
客户端必须找到并注册每个对象分别,如果每个对象有多个事件,那么每个事件需要单独订阅。 事件聚合器作为事件的单一来源, 它注册了许多对象的所有事件允许客户端仅使用聚合器注册。
何时使用事件聚合器模式
标题 | 类别 | 难度 |
---|---|---|
事件异步(Event-based Asynchronous design) | 并发 | 中等难度,性能 |
基于事件的异步模式提供了多线程应用程序的优势,同时隐藏了许多应用程序
的多线程设计中固有的复杂问题。 使用支持此模式的类可以允许您:
使用基于事件的异步模式
标题 | 类别 | 难度 |
---|---|---|
基于事件驱动的体系结构(Event Driven Architecture design) | 结构型 | 困难,响应式 |
发送或通知对象状态的变化,给使用了基于事件驱动的体系结构其他应用。
标题 | 类别 | 难度 |
---|---|---|
事件队列(event-queue) | 并发 | 简单难度,队列 |
事件队列是一个很好的模式,如果你有一个有限的可访问资源(例如:音频或数据库),但您需要处理所有要使用的请求。它将所有请求放入队列中并异步处理。当事件完成时,从队列中移除,并给下一个事件提供资源。
使用事件队列模式
标题 | 类别 | 难度 |
---|---|---|
事件捕捉(event sourcing design) | 结构型 | 困难难度,性能 |
不要仅将数据的当前状态存储在域中,而是使用追加专用存储来记录对该数据执行的所有操作。贮存系统可以让领域对象物质化(在json文件中保存对对象的操作)。 这可以简化复杂域中的任务,避免在同步数据模型和业务领域的同时,同时提高性能,可扩展性和响应能力。 它还可以为事务数据提供一致性,并保持完整的审计跟踪和历史记录,从而可以实现补偿操作。
何时使用事件捕捉模式
标题 | 类别 | 难度 |
---|---|---|
执行环绕(execute around design) | 其他 | 简单难度,术语 |
执行围绕术语的意思是释放用户的某些动作,应始终在业务方法之前和之后执行。 一个很好的例子
这是资源分配和释放,让用户只需要关系如何操作资源。
何时使用执行环绕
标题 | 类别 | 难度 |
---|---|---|
扩展对象( Extension objects) | 行为型 | 中等难度 |
预计在将来需要扩展对象的接口。 额外接口由扩展对象定义。
在以下情况下使用扩展对象模式
标题 | 类别 | 难度 |
---|---|---|
外观模式( facade design)) | 结构型 | 简单难度,GOF |
为子系统中的一组接口提供统一的接口。
外观模式定义了一个更高级别的界面,使子系统更易于使用。
现实例子
金矿如何运作? “矿工们下去挖金了!” 你说。 这就是你所相信的,因为你正在使用一个简单的界面,goldmine在外面提供,在内部它必须做很多事情才能实现。 复杂子系统的这个简单界面是一个外观。
字面意思
外观模式为复杂子系统提供简化的界面。
维基解释
外观是一个对象,它提供了一个更简单的界面,用于更大量的代码,例如类库。
编程例子
从上面我们的金矿做例子。 在这里,我们有矮人的矿工级别
//矮人工人
public abstract class DwarvenMineWorker {
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenMineWorker.class);
public void goToSleep() {
LOGGER.info("{} goes to sleep.", name());
}
public void wakeUp() {
LOGGER.info("{} wakes up.", name());
}
public void goHome() {
LOGGER.info("{} goes home.", name());
}
public void goToMine() {
LOGGER.info("{} goes to the mine.", name());
}
private void action(Action action) {
switch (action) {
case GO_TO_SLEEP:
goToSleep();
break;
case WAKE_UP:
wakeUp();
break;
case GO_HOME:
goHome();
break;
case GO_TO_MINE:
goToMine();
break;
case WORK:
work();
break;
default:
LOGGER.info("Undefined action");
break;
}
}
public void action(Action... actions) {
for (Action action : actions) {
action(action);
}
}
public abstract void work();
public abstract String name();
static enum Action {
GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK
}
}
//矮人隧道挖掘机操作者
public class DwarvenTunnelDigger extends DwarvenMineWorker {
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenTunnelDigger.class);
@Override
public void work() {
LOGGER.info("{} creates another promising tunnel.", name());
}
@Override
public String name() {
return "Dwarven tunnel digger";
}
}
//矮人黄金挖掘机操作者
public class DwarvenGoldDigger extends DwarvenMineWorker {
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenGoldDigger.class);
@Override
public void work() {
LOGGER.info("{} digs for gold.", name());
}
@Override
public String name() {
return "Dwarf gold digger";
}
}
//矮人矿车操作者
public class DwarvenCartOperator extends DwarvenMineWorker {
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenCartOperator.class);
@Override
public void work() {
LOGGER.info("{} moves gold chunks out of the mine.", name());
}
@Override
public String name() {
return "Dwarf cart operator";
}
}
为了操作所有这些金矿工人,我们有一个外观
public class DwarvenGoldmineFacade {
private final List workers;
public DwarvenGoldmineFacade() {
workers = new ArrayList<>();
workers.add(new DwarvenGoldDigger());
workers.add(new DwarvenCartOperator());
workers.add(new DwarvenTunnelDigger());
}
public void startNewDay() {
makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE);
}
public void digOutGold() {
makeActions(workers, DwarvenMineWorker.Action.WORK);
}
public void endDay() {
makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP);
}
private static void makeActions(Collection workers,
DwarvenMineWorker.Action... actions) {
for (DwarvenMineWorker worker : workers) {
worker.action(actions);
}
}
}
现在使用这个外观
DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade();
facade.startNewDay();
// Dwarf gold digger wakes up.
// Dwarf gold digger goes to the mine.
// Dwarf cart operator wakes up.
// Dwarf cart operator goes to the mine.
// Dwarven tunnel digger wakes up.
// Dwarven tunnel digger goes to the mine.
facade.digOutGold();
// Dwarf gold digger digs for gold.
// Dwarf cart operator moves gold chunks out of the mine.
// Dwarven tunnel digger creates another promising tunnel.
facade.endDay();
// Dwarf gold digger goes home.
// Dwarf gold digger goes to sleep.
// Dwarf cart operator goes home.
// Dwarf cart operator goes to sleep.
// Dwarven tunnel digger goes home.
// Dwarven tunnel digger goes to sleep.
何时使用外观模式
标题 | 类别 | 难度 |
---|---|---|
工厂套件( Factory Kit design) | 创建型 | 简单难度,函数式 |
使用分离的构建器和工厂接口定义一个内容不可变化的工厂。
何时使用工厂套件模式
标题 | 类别 | 难度 |
---|---|---|
工厂方法( Factory Method design) | 创建型 | 简单难度,GOF |
Virtual Constructor
定义一个用于创建一个对象的接口,但是让子类决定要实例化哪个类。 工厂方法允许类推迟
实例化到子类。
真实世界
铁匠制造武器。 精灵需要精灵武器和兽人需要兽医武器。 根据手头的客户,正确的类型被铁匠制造。
字面意思
它提供了一种将实例化逻辑委托给子类的方法。
维基解释
在基于类的编程中,工厂方法模式是使用工厂方法来处理创建对象的问题的创建模式,而不必指定将要创建的对象的确切类。 这可以通过调用工厂方法(在接口中指定并由子类实现)来实现,或者在基类中实现,并且可选地被派生类覆盖,而不是调用构造函数。
编程例子
以我们的铁匠为例。 首先我们有一个铁匠接口和一些实现
public interface Blacksmith {
Weapon manufactureWeapon(WeaponType weaponType);
}
public class ElfBlacksmith implements Blacksmith {
public Weapon manufactureWeapon(WeaponType weaponType) {
return new ElfWeapon(weaponType);
}
}
public class OrcBlacksmith implements Blacksmith {
public Weapon manufactureWeapon(WeaponType weaponType) {
return new OrcWeapon(weaponType);
}
}
现在,由于客户来了,铁匠被要求制造符合客户武器类型的武器
Blacksmith blacksmith = new ElfBlacksmith();
blacksmith.manufactureWeapon(WeaponType.SPEAR);
blacksmith.manufactureWeapon(WeaponType.AXE);
// 创建精灵武器
何时使用“工厂方法”模式
标题 | 类别 | 难度 |
---|---|---|
功能切换(Feature Toggle design) | 行为型 | 简单难度 |
Feature Flag
根据属性或分组切换代码执行路径。 允许新功能被发布,测试并推出。 如果需要,可以快速切换回旧功能。 应该指出的是,这种模式,
可以轻松引入代码复杂性。 还有一个令人担忧的是,切换最终的旧功能
逐步淘汰不会被删除,从而导致冗余代码的气味和维护成本提高。
使用功能切换模式
标题 | 类别 | 难度 |
---|---|---|
流畅的接口(Fluent Interface design) | 其他 | 中等难度,函数式 |
流畅的接口提供了一个易于阅读,流畅的接口,通常会模仿一个领域特定的语言。 使用这种模式可以生成像人类语言一样可供阅读的代码
流畅的接口可以使用如下方式来实现
使用流畅的界面模式
标题 | 类别 | 难度 |
---|---|---|
流量模式(flux design) | 持久层 | 中等难度 |
Flux避开MVC设计模式,有利于单向数据流。 当一个
用户与视图进行交互,视图通过中央传播动作调度员,到持有应用程序的数据和业务的各种商业逻辑,更新所有受影响的视图。
博主思考
一个客户端程序如Adnroid程序,定义用户触发的事件, 通过Stroe让模型和视图分离,这在单事件触发单页面的时候体会不出好处,反而会增加定义事件Action的负担,但是单A页面触发的事件,引起多个B,C,View 视图的UI变化时,便能很清晰的追溯整个事件流的过程。
使用流量模式时
标题 | 类别 | 难度 |
---|---|---|
享元模式(flyweight design) | 持久层 | GOF,中等难度,性能 |
使用共享技术有效支持大量细粒度的对象
真实世界
炼金术士的商店货架上摆满了魔法药水。很多药水都是一样的,所以不需要为它们创建新的对象。相反,一个对象实例可以代表多个货架项目,因此内存占用仍然很小。
字面意思
它通过与相似对象共享尽可能减少内存使用或计算开销。
维基描述
在计算机编程中,享元模式是一种软件设计模式。 享元模式通过与其他类似对象尽可能多的共享数据来最小化内存使用的对象; 当简单的重复表示将使用不可接受的内存量时,它是大量使用对象的一种方式。
编程例子
从上面翻译我们的炼金术士商店示例。 首先我们有不同的药水类型
//药剂
public interface Potion {
void drink();
}
//回复药剂(补血?)
public class HealingPotion implements Potion {
private static final Logger LOGGER = LoggerFactory.getLogger(HealingPotion.class);
@Override
public void drink() {
LOGGER.info("You feel healed. (Potion={})", System.identityHashCode(this));
}
}
//升水药剂(补蓝?)
public class HolyWaterPotion implements Potion {
private static final Logger LOGGER = LoggerFactory.getLogger(HolyWaterPotion.class);
@Override
public void drink() {
LOGGER.info("You feel blessed. (Potion={})", System.identityHashCode(this));
}
}
//隐形药剂
public class InvisibilityPotion implements Potion {
private static final Logger LOGGER = LoggerFactory.getLogger(InvisibilityPotion.class);
@Override
public void drink() {
LOGGER.info("You become invisible. (Potion={})", System.identityHashCode(this));
}
}
然后是实际的享元对象,它是用于创建药水的工厂
public class PotionFactory {
private final Map potions;
public PotionFactory() {
potions = new EnumMap<>(PotionType.class);
}
Potion createPotion(PotionType type) {
Potion potion = potions.get(type);
if (potion == null) {
switch (type) {
case HEALING:
potion = new HealingPotion();
potions.put(type, potion);
break;
case HOLY_WATER:
potion = new HolyWaterPotion();
potions.put(type, potion);
break;
case INVISIBILITY:
potion = new InvisibilityPotion();
potions.put(type, potion);
break;
default:
break;
}
}
return potion;
}
}
如下使用
PotionFactory factory = new PotionFactory();
factory.createPotion(PotionType.INVISIBILITY).drink(); // You become invisible. (Potion=6566818)
factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364)
factory.createPotion(PotionType.INVISIBILITY).drink(); // You become invisible. (Potion=6566818)
factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potion=1104106489)
factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potion=1104106489)
factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364)
享元模式的有效性在很大程度上取决于如何使用和它在哪里使用。 当以下所有的时候应用享元模式
真正
标题 | 类别 | 难度 |
---|---|---|
前台控制(front controller design) | 持久层 | 中等难度 |
为网站的所有请求引入共同的处理器。 我们可以封装常见的功能,如安全性,
国际化,路由和记录在一个单一的地方。
使用前端控制器模式
标题 | 类别 | 难度 |
---|---|---|
被监视的挂起(Guarded Suspension design) | 并发 | 简单难度 |
当想要对处于不在合适状态的对象执行方法时,可以适用被监视的挂起来处理一些情况
当开发人员知道方法执行将在一段有限的时间内被阻止时,使用Guarded Suspension模式
标题 | 类别 | 难度 |
---|---|---|
半同步半异步(Half-Sync/Half-Async design) | 并发 | 中等难度 |
半同步/半异步模式,从异步中分离出同步的I/O,简化并行编程工作,没有降低执行效率。
使用半同步/半异步模式
标题 | 类别 | 难度 |
---|---|---|
六角结构,又名端口&适配器结构(Hexagonal Architecture) | 并发 | 专家难度 |
不同的用户可以相似的驱动应用程序,自动化测试或批处理脚本驱动,并与其最终的运行时设备和数据库隔离开发和测试。
使用六角架构模式
标题 | 类别 | 难度 |
---|---|---|
拦截过滤(Intercepting Filter) | 行为型 | 中等难度 |
提供可插拔过滤器进行必要的预处理和对从客户端到目标的请求进行后处理
使用拦截过滤模式
标题 | 类别 | 难度 |
---|---|---|
解释器模式(Interpreter) | 行为型 | GOF,中等难度 |
给定一种语言,根据解释器定义其语法的表示。
当有语言时使用解释器模式解释,你可以用解释器表示语言中的语句作为抽象语法
树木。 解释者模式如下最有效
标题 | 类别 | 难度 |
---|---|---|
迭代器模式(iterator) | 行为型 | GOF,简单难度 |
Cursor
何时使用迭代器模式
标题 | 类别 | 难度 |
---|---|---|
层模式(layers) | 结构型 | Spring,中等难度 |
层是一种软件的体系结构风格。
将应用程序划分成几个不同层次。
使用层架构时
标题 | 类别 | 难度 |
---|---|---|
懒加载(Lazy Loading) | 其他 | 术语,简单难度,性能 |
懒加载是一种常见的模式用于延迟初始化一个对象,直到需要的时候。 它可以有助于应用的运作效率适当使用。
使用Lazy Loading术语
标题 | 类别 | 难度 |
---|---|---|
标记接口(Lazy Loading) | 设计 | 简单难度 |
使用空接口作为标记来区分处理对象。
何时使用标记界面图案
标题 | 类别 | 难度 |
---|---|---|
中间人模式(mediator) | 行为型 | GOF,中等难度 |
定义一个对象,用来封装一组对象交互。
中介者通过保持其他对象的引用来促进松耦合,
它可以让你独立地改变他们的互动。
使用中介模式
标题 | 类别 | 难度 |
---|---|---|
备忘录模式(Memento) | 行为型 | GOF,中等难度 |
Token
不侵犯封装,捕获和外部化
对象的内部状态,以便稍后可以将对象恢复到此状态。
当使用备忘者模式时
标题 | 类别 | 难度 |
---|---|---|
消息渠道(Message Channel) | 交互 | EIP,Apache Camel™ |
当两个应用程序使用消息系统进行通信时,他们通过使用逻辑地址进行通信
的系统,所谓的消息通道。
使用消息通道模式
标题 | 类别 | 难度 |
---|---|---|
模型-视图-控制器(Model-View-Controller) | 持久层 | 中等难度 |
将用户界面分为三个互连的组件:
模型,视图和控制器。 让模型管理数据,视图显示数据,控制器调停更新数据并重新绘制
显示。
使用模型 - 视图 - 控制器模式
标题 | 类别 | 难度 |
---|---|---|
模型-视图-主持人(Model-View-Presenter) | 持久层 | 中等难度 |
应用“关注点分离”原则,允许开发人员构建和测试用户界面。
在下列使用MVP情况
* 当你想改进表示逻辑中的“分离关注”原则时
* 当用户界面开发和测试是必要的。
标题 | 类别 | 难度 |
---|---|---|
模块化(Module) | 创建型 | 简单难度 |
模块模式用于实现由模块化编程定义的软件模块的概念,在具有不完全直接支持概念的编程语言中。
模块模式可以被认为是一种创意模式和一种结构模式。 它管理其他元素的创建和组织,并将其组织为结构模式。
应用此模式的对象可以提供等效的命名空间,为静态类或具有更干净,更简洁的语法和语义的静态成员提供初始化和完成过程。
标题 | 类别 | 难度 |
---|---|---|
Monad(这个怎么翻译?) | 其他 | 专家难度,函数式 |
基于线性代数的Monad模式表现出一步一步的链接操作的样子。只要保证“同类型”约束,绑定函数可以描述为将其输出传递给另一个输入。 正式地,monad由一个类型构造函数M和两个操作符组成:
bind - 将monadic对象和一个函数从plain对象转换为monadic值并返回monadic值
return - 它采用普通类型的对象,并返回包含在一个monadic值中的对象。
(这方面需要函数式编程方面的知识。。博主只使用过rxjava,有点难描述)
在以下任何一种情况下使用Monad
标题 | 类别 | 难度 |
---|---|---|
单一的状态(monostate) | 创建型 | 简单难度 |
Borg
执行一个行为,例如在所有实例之间分享相同的状态(这里分享request)
使用Monostate模式
标题 | 类别 | 难度 |
---|---|---|
多例模式(Multiton) | 创建型 | 简单难度 |
Registry
何时使用多例模式
标题 | 类别 | 难度 |
---|---|---|
静音模式(mute) | 其他 | 简单难度,术语 |
提供一个模板来抑制任何被声明但不能发生或只应该被记录的异常;
同时执行一些业务逻辑。 该模板无需编写重复的try-catch
块。
何时使用这个术语
标题 | 类别 | 难度 |
---|---|---|
互斥模式(Mutex) | 并发 | 中等难度 |
Mutual Exclusion Lock
Binary Semaphore
何时使用互斥
标题 | 类别 | 难度 |
---|---|---|
裸露的对象(Naked Objects) | 结构型 | 专家难度 |
裸露的对象架构模式非常适合快速原型。 使用模式,只需要编写域对象,
一切都是由框架自动生成的。
使用裸体对象模式
标题 | 类别 | 难度 |
---|---|---|
空对象(Null Object) | 行为型 | 简单难度 |
在大多数面向对象的语言中,如Java或C#,引用可能为null。 在调用任何方法之前需要检查这些引用以确保它们不为空
,因为通常无法调用空引用的方法。 而不是使用空引用来表示没有对象(例如,不存在的客户),使用一个对象实现预期的接口,但其方法体是空的。该这种方法优于一个工作的默认实现是一个Null对象是非常可预测的,没有副作用:它什么都不做。
何时使用空对象模式
标题 | 类别 | 难度 |
---|---|---|
母对象(object-mother) | 创建型 | 简单难度 |
使用分离的构建器和工厂的接口定义不可变的内容。
何时使用母对象
Answer by David Brown to the stackoverflow question: What is an ObjectMother?
c2wiki - Object Mother
Nat Pryce - Test Data Builders: an alternative to the Object Mother pattern
标题 | 类别 | 难度 |
---|---|---|
对象池(object-pool) | 创建型 | 简单难度,性能 |
当对象创建成本高昂时,并且它只需要在短时间内被使用,这时对象池模式是有用处的。对象池为实例化的对象提供了缓存,跟踪哪些对象正在使用,哪些是可用的。
使用对象池模式
标题 | 类别 | 难度 |
---|---|---|
Observer(观察者模式) | 创建型 | 简单难度,GOF,响应式 |
Dependents, Publish-Subscribe
定义对象之间的一对多依赖关系, 被观察的对象状态改变,其所有依赖关系将被通知和更新自动。
在以下情况下使用观察者模式
标题 | 类别 | 难度 |
---|---|---|
页对象(page-object) | 测试 | 中等难度 |
页面对象封装了UI,隐藏应用程序(通常是Web应用程序)的底层UI小部件,并提供特定于应用程序的API,以允许操作测试所需的UI组件。 在这样做时,它允许测试类本身专注于测试逻辑。
何时使用页面对象模式
标题 | 类别 | 难度 |
---|---|---|
页对象(page-object) | 结构型 | 简单难度 |
根据需要从服务器发送部分响应。 客户端将指定它需要的字段,而不是要求服务端提供资源的所有细节。
使用部分响应模式
标题 | 类别 | 难度 |
---|---|---|
poison-pill(不知道怎么翻译)) | 其他 | 中等难度,响应式 |
poison-pill允许提供的已知预定义数据项,生产者与消耗者分离,并提供优雅的方式结束进程
何时使用poison-pil术语
标题 | 类别 | 难度 |
---|---|---|
Private Class Data(私有类数据) | 其他 | 简单难度,术语 |
私人数据设计模式旨在减少曝光属性通过限制其可见性。 它减少了类的数量
通过将属性封装在单个Data对象中。
何时使用私有类数据模式
标题 | 类别 | 难度 |
---|---|---|
生产者-消费者(producer-consumer) | 并发 | 中等难度,I/O,响应 |
生产者消费者设计模式是一种经典的并发模式,降低了生产者与消费者之间的耦合,通过分离实际的工作执行。
何时使用生产者消费者术语
标题 | 类别 | 难度 |
---|---|---|
承诺(promise) | 并发 | 中等难度,函数,响应 |
承诺模式代表一个值但事先却不需要知道它。允许你通过承诺模式关联一个异步动作的最终成功值或失败的原因。提供了一种异步访问的方式,但表现出同步执行的样子
某些工作需要异步完成时,承诺模式适用于并行编程
和:
标题 | 类别 | 难度 |
---|---|---|
属性(property) | 创建型 | 简单 |
何时使用属性模式
标题 | 类别 | 难度 |
---|---|---|
原型(prototype) | 创建型 | GOF,简单 |
使用原型指定要创建的对象的种类实例,并通过复制此原型创建新对象。
真实世界
还记得多莉吗 被克隆的羊! 让我们不了解细节,但这里的关键在于它是如何克隆的。
字面意思
通过克隆创建基于现有对象的对象。
维基解释
原型图是软件开发中的创建型设计模式。 当要创建的对象的类型由原型实例确定时,它将被克隆以生成新对象。
简而言之,它允许您创建现有对象的副本并根据需要进行修改,而不是从头开始创建对象并进行设置。
编程例子
在Java中,可以通过从Object
实现Cloneable
并覆盖clone
来轻松实现
class Sheep implements Cloneable {
private String name;
public Sheep(String name) { this.name = name; }
public void setName(String name) { this.name = name; }
public String getName() { return name; }
@Override
public Sheep clone() throws CloneNotSupportedException {
return new Sheep(name);
}
}
然后可以像下面那样进行克隆
Sheep original = new Sheep("Jolly");
System.out.println(original.getName()); // Jolly
Sheep cloned = original.clone();
cloned.setName("Dolly");
System.out.println(cloned.getName()); // Dolly
当系统应独立于其产品的创建,组成和代表的方式时,使用原型模式; 和
标题 | 类别 | 难度 |
---|---|---|
proxy(代理) | 结构型 | GOF,简单 |
为另一个对象提供代理或占位符来控制访问它
真实例子
想象一个法术塔,允许当地的巫师去研究他们的法术。 象牙塔只能通过代理访问,确保只有前三个巫师才能进入。 这里代理代表了塔的功能,并添加了访问控制。
字面意思
使用代理模式,一个类代表另一个类的功能。
维基描述
代理,最通用的形式是一个类作为其他东西的接口。 代理是由客户端调用来访问幕后真实服务对象的包装器或代理对象。 使用代理可以简单地转发到真实的对象,或者可以提供额外的逻辑。 在代理中,可以提供额外的功能,例如当真实对象上的操作是资源密集型时的缓存,或者在调用真实对象的操作之前检查前提条件。
编程例子
从上面我们的网页上股塔示例。 首先我们有巫师塔接口和象牙塔类
public interface WizardTower {
void enter(Wizard wizard);
}
public class IvoryTower implements WizardTower {
private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class);
public void enter(Wizard wizard) {
LOGGER.info("{} enters the tower.", wizard);
}
}
简单的巫师类
public class Wizard {
private final String name;
public Wizard(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
我们通过代理控制巫师塔的进入
public class WizardTowerProxy implements WizardTower {
private static final Logger LOGGER = LoggerFactory.getLogger(WizardTowerProxy.class);
private static final int NUM_WIZARDS_ALLOWED = 3;
private int numWizards;
private final WizardTower tower;
public WizardTowerProxy(WizardTower tower) {
this.tower = tower;
}
@Override
public void enter(Wizard wizard) {
if (numWizards < NUM_WIZARDS_ALLOWED) {
tower.enter(wizard);
numWizards++;
} else {
LOGGER.info("{} is not allowed to enter!", wizard);
}
}
}
这里是塔进入场景
WizardTowerProxy proxy = new WizardTowerProxy(new IvoryTower());
proxy.enter(new Wizard("Red wizard")); // 进去.
proxy.enter(new Wizard("White wizard")); //进去.
proxy.enter(new Wizard("Black wizard")); // 进去.
proxy.enter(new Wizard("Green wizard")); // 进不去!
proxy.enter(new Wizard("Brown wizard")); // 进不去!
通过代理模式来使用更多变化或复杂的对象。 这里
是代理模式适用的几种常见情况
标题 | 类别 | 难度 |
---|---|---|
发布-订阅(publish-subscribe) | 结构型 | EIP,Apache Camel™ |
发送者发送广播信息至对相关信息感兴趣的接收者
何时使用发布订阅频道模式
标题 | 类别 | 难度 |
---|---|---|
基于队列的负载均衡(queue-load-leveling) | 其他 | 中等难度,性能 |
使用一个队列,作为一个任务和它调用的服务之间的缓冲区,以便顺利进行可能导致服务失败或任务超时的间歇性重负载。这种模式可以帮助最大限度地减少需求峰值对可用性和响应能力的影响
用于任务和服务。
标题 | 类别 | 难度 |
---|---|---|
响应器(reactor) | 并发 | 专家难度,I/O |
反应器设计模式处理由一个或多个客户端同时传递到应用程序的服务请求。 应用程序可以注册特定的事件处理器来处理特定事件。 反应器持有事件分发器,并分发事件给特定的事件处理器。
使用反应器模式
标题 | 类别 | 难度 |
---|---|---|
响应器(reactor) | 并发 | 中等难度,性能 |
假设我们有一个具有上面详细描述的基本约束的共享内存区域。 可以保护互斥互斥体之后的共享数据,在这种情况下,两个线程不能同时访问数据。 但是,这个解决方案不是最佳的,因为读取器R1可能有锁,然后另一个读取器R2请求访问。 在开始自己的阅读操作之前,R2等待R1完成是愚蠢的; 相反,R2应该立即开始。 这是Reader Writer Lock模式的动机。
应用程序需要增加多线程的资源同步性能,特别是有混合的读/写操作。
Readers–writer lock
Readers–writers_problem
标题 | 类别 | 难度 |
---|---|---|
仓库(repository) | 持久层 | 中等难度,spring |
在域和数据映射之间添加存储库层层隔离域对象与数据库访问代码的细节
以最小化查询代码的散布和重复。 存储库模式是特别适用于域类大或重的系统
利用查询。
何时使用Repository模式
标题 | 类别 | 难度 |
---|---|---|
资源获取初始化(resource-acquisition-is-initialization) | 其他 | 简单难度,术语 |
编程例子
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
public static void main(String[] args) throws Exception {
try (SlidingDoor slidingDoor = new SlidingDoor()) {
LOGGER.info("Walking in.");
}
try (TreasureChest treasureChest = new TreasureChest()) {
LOGGER.info("Looting contents.");
}
}
}
用资源获取初始化模式
标题 | 类别 | 难度 |
---|---|---|
信号(semaphore) | 并发 | 中等难度 |
创建一个中介对资源池的访问的锁。在有限数量的线程条件下,在创建时指定一定数量的
信号量,可以在任何给定的时间访问资源。一个只允许一个并发访问资源的信号量
被称为二进制信号量。
何时使用信号量时
标题 | 类别 | 难度 |
---|---|---|
仆人(servant) | 结构型 | 简单难度 |
仆人用于为一组类提供一些行为。而不是在每个类中定义该行为 - 或者当我们无法确定
在普通父类中使用这种行为 , 它在仆人中定义一次。
时使用仆人模式
标题 | 类别 | 难度 |
---|---|---|
服务层(service-layer) | 结构型 | 中等难度 |
服务层是域逻辑的抽象。通常应用程序需要多种接口来存储和存储数据
实现逻辑:数据加载器,用户界面,集成网关和其他。 尽管它们的目的不同,但是这些接口通常需要通用
与应用程序的交互访问和操纵其数据并调用其业务逻辑。 服务层实现了这一角色。
何时使用服务层模式
标题 | 类别 | 难度 |
---|---|---|
服务定位(service-locator) | 结构型 | 简单难度,性能 |
封装服务层的获取执行过程。
服务定位器模式适用于任何时候使用通常是冗余的JNDI来定位/获取各种服务
和昂贵的查询。 服务定位器模式解决了这个昂贵的问题通过使用缓存技术来查找。 为了第一次
请求特定服务,服务定位器在JNDI中查找,取出相关服务,然后最终缓存此服务对象。 现在进一步
通过服务定位器查找相同的服务在其缓存中完成在很大程度上提高了应用的性能。
标题 | 类别 | 难度 |
---|---|---|
单例(singleton) | 结构型 | 简单难度,性能 |
确保一个类只有一个实例,并提供一个全局点访问它
真实例子
只有一个象牙塔,巫师研究他们的魔法。 巫师一直使用同样的魔法象牙塔。 这里的象牙塔是单例。
字面意思
确保只创建一个特定类的一个对象。
维基解释
在软件工程中,单例模式是将类的实例化限制为一个对象的软件设计模式。 当需要一个对象来协调整个系统的动作时,这是非常有用的。
编程例子
Joshua Bloch,Effective Java 2nd Edition第18页
单元素枚举类型是实现单例的最佳方法
public enum EnumIvoryTower {
INSTANCE;
@Override
public String toString() {
return getDeclaringClass().getCanonicalName() + "@" + hashCode();
}
}
使用
EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE;
EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE;
assertEquals(enumIvoryTower1, enumIvoryTower2); // true
不安全单例
public final class IvoryTower {
private IvoryTower() {}
private static final IvoryTower INSTANCE = new IvoryTower();
public static IvoryTower getInstance() {
return INSTANCE;
}
安全懒加载单例
public final class ThreadSafeLazyLoadedIvoryTower {
private static ThreadSafeLazyLoadedIvoryTower instance;
private ThreadSafeLazyLoadedIvoryTower() {
// to prevent instantiating by Reflection call
if (instance != null) {
throw new IllegalStateException("Already initialized.");
}
}
public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() {
if (instance == null) {
instance = new ThreadSafeLazyLoadedIvoryTower();
}
return instance;
}
}
双重检查锁单例
public final class ThreadSafeDoubleCheckLocking {
private static volatile ThreadSafeDoubleCheckLocking instance;
private ThreadSafeDoubleCheckLocking() {
// to prevent instantiating by Reflection call
if (instance != null) {
throw new IllegalStateException("Already initialized.");
}
}
public static ThreadSafeDoubleCheckLocking getInstance() {
ThreadSafeDoubleCheckLocking result = instance;
//检查单例实例是否已初始化。 如果它被初始化,那么我们可以返回实例。
if (result == null) {
//它没有初始化,但是我们不能确定,因为一些其他线程可能已初始化它
// 同时。 所以要确保我们需要锁定一个对象以获得互斥。
synchronized (ThreadSafeDoubleCheckLocking.class) {
//再次将实例分配给局部变量,以检查它是否被某个其他线程初始化
//当前线程被阻止进入锁定区域。 如果它被初始化,那么我们可以
//返回之前创建的实例,就像以前的空检查一样。
result = instance;
if (result == null) {
//实例还没有被初始化,所以我们可以安全地(没有其他线程可以进入这个区域)
//创建一个实例,使其成为我们的单例实例。
instance = result = new ThreadSafeDoubleCheckLocking();
}
}
}
return result;
}
}
内部静态类成员持有实例
public final class InitializingOnDemandHolderIdiom {
private InitializingOnDemandHolderIdiom() {}
public static InitializingOnDemandHolderIdiom getInstance() {
return HelperHolder.INSTANCE;
}
private static class HelperHolder {
private static final InitializingOnDemandHolderIdiom INSTANCE =
new InitializingOnDemandHolderIdiom();
}
}
何时Singleton模式
标题 | 类别 | 难度 |
---|---|---|
规范(specification) | 行为型 | 简单难度 |
规范模式分离如何匹配一个候选的对象组。 它是有作用的对于,值的校验和绑定命令。
何时使用规格图样
标题 | 类别 | 难度 |
---|---|---|
状态(state) | 行为型 | 中等难度,GOF |
允许对象在其内部状态时更改其行为变化。 该对象将会改变其类。
在以下任何一种情况下使用状态模式
标题 | 类别 | 难度 |
---|---|---|
一步一步创建者(step-builder) | 创建型 | 中等难度 |
Builder模式的扩展,完全引导用户创建对象,没有混淆的机会。
用户体验将会因为只能看到下一步方法可用,而不是构建方法直到构建对象才是正确的时间,因此将会得到更多的改进。
当创建复杂对象的算法应该独立于组成对象的部分以及组合方式时,使用Step Builder模式,构造过程必须允许在构造顺序的过程中构造的对象的不同表示。
标题 | 类别 | 难度 |
---|---|---|
策略(strategy) | 行为型 | GOF,简单难度 |
定义一系列算法,封装每一个算法,确保他们可以通用。 策略模式让算法的变化独立与客户端的使用。
使用策略模式
标题 | 类别 | 难度 |
---|---|---|
模板方法(template-method) | 行为型 | GOF,简单难度 |
在操作中定义算法的骨架,推迟一些算法步骤到子类。 模板方法让子类重新定义某些步骤
,一种不改变算法结构的方式。
何时使用模板方法模式
标题 | 类别 | 难度 |
---|---|---|
线程池(thread-pool) | 行为型 | 性能,中等难度 |
通常情况下要执行的任务是短暂的并且任务数量大。 为每个任务创建一个新线程
系统花费更多的时间而不是执行实际任务。 线程池通过重用现有的线程来解决这个问题
并消除创建新线程的延迟。
使用线程池模式
标题 | 类别 | 难度 |
---|---|---|
节流(throttling) | 行为型 | 简单难度 |
在超过服务端分配限制的情况下,限制客户端的访问
应使用节流模式:
标题 | 类别 | 难度 |
---|---|---|
线程本地存储(Thread Local Storage) | 并发型 | 中等难度 |
将变量保存到线程,以免被其他线程损坏。 如果您在Callable对象或Runnable对象中使用不是只读的类变量或静态变量,则需要这样做。
在以下任何情况下使用线程本地存储
标题 | 类别 | 难度 |
---|---|---|
宽容阅读(tolerant-reader) | 交互 | 简单难度 |
宽容阅读是一种有助于创建的集成健壮的通信系统。 这个想法是尽可能的宽容从另一个服务读取数据。 这样,当通信模式改变,接收者一定不会崩溃。
使用宽容阅读模式
标题 | 类别 | 难度 |
---|---|---|
双胞胎(twin) | 创建型 | 中等难度 |
双模式是一种设计模式,提供了一个标准的解决方案去模拟多继承
何时使用双模式术语
标题 | 类别 | 难度 |
---|---|---|
值对象(value-object) | 创建型 | 简单难度 |
提供值语义而不是引用语义的对象。
这意味着价对象的equal不是等于两个对象相同。 两个值对象是
当它们具有相同的值时,不一定是相同的对象。
何时使用值对象
标题 | 类别 | 难度 |
---|---|---|
访问者(visitor) | 行为型 | GOF,中等难度 |
表示要对对象的元素执行的操作结构体。 访问者可以在不更改类的情况下定义新操作
的操作元素。
何时使用访客模式
[1]设计模式沉思录
[2]MVC vs. MVP vs. MVVM on Android
[3]java 设计模式