1.責(zé)任鏈模式(Chain of Responsibility)
2.命令模式(Command)
3.解釋器模式(Interpreter)
4.迭代器模式(Iterator)
5.中介者模式(Mediator)
6.備忘錄模式(Memento)
7.觀察者模式(Observer)
8.狀態(tài)模式(State)
9.策略模式(Strategy)
10.模板方法模式(Template Method)
11.訪問者模式(Visitor)
行為型關(guān)注點(diǎn):關(guān)注對(duì)象之間的交互通信(方法級(jí)別)
模板方法模式(Template Method)
定義:
定義一個(gè)操作中的算法骨架,而將一些步驟延遲到子類中。
通俗來說
在父類中設(shè)計(jì)主流程,將主流程步驟的實(shí)現(xiàn)方法交給子類來實(shí)現(xiàn)。這樣就實(shí)現(xiàn)了方法的延遲。
角色
- 抽象模板類(AbstractClass):,就是一個(gè)抽象模板,定義算法的骨架。
- 具體實(shí)現(xiàn)類(ConcreteClass):實(shí)現(xiàn)算法其中某些過程方法,每個(gè)具體實(shí)現(xiàn)類給出算法的不同實(shí)現(xiàn),從而使得抽象模板中骨架方法的實(shí)現(xiàn)各不相同。

抽象模板類:
public abstract class AbstractClass {
public abstract void primitiveOperation1();
public abstract void primitiveOperation2();
// 模板方法,給出了邏輯的骨架
// 而邏輯的組成是一些相應(yīng)的抽象操作,他們都推遲到子類實(shí)現(xiàn)
public void templateMethod() {
primitiveOperation1();
primitiveOperation2();
System.out.println("");
}
}
具體實(shí)現(xiàn)類A:
public class ConcreteClassA extends AbstractClass {
@Override
public void primitiveOperation1() {
System.out.println("具體類A方法1實(shí)現(xiàn)");
}
@Override
public void primitiveOperation2() {
System.out.println("具體類A方法2實(shí)現(xiàn)");
}
}
具體實(shí)現(xiàn)類B:
public class ConcreteClassA extends AbstractClass {
@Override
public void primitiveOperation1() {
System.out.println("具體類B方法1實(shí)現(xiàn)");
}
@Override
public void primitiveOperation2() {
System.out.println("具體類B方法2實(shí)現(xiàn)");
}
}
客戶端調(diào)用類
public class Client {
public static void main(String[] args) {
AbstractClass c;
c = new ConcreteClassA();
c.templateMethod();
c = new ConcreteClassB();
c.templateMethod();
}
}
優(yōu)點(diǎn):
- 1.模板方法模式通過把不變的行為搬移到父類,去除了子類中重復(fù)的代碼。
- 2.通過子類擴(kuò)展增加新的行為,符合開閉原則。
應(yīng)用場(chǎng)景:
多個(gè)子類有共有的方法,并且邏輯基本相同。
重構(gòu)時(shí),模板方法是一個(gè)經(jīng)常使用的方法。把相同代碼抽取到父類中,然后通過構(gòu)造函數(shù)約束其行為。
策略模式(Strategy)
定義
策略模式定義了一系列算法,并將每個(gè)算法封裝起來,讓它們之間可以互相替換,讓算法的變化獨(dú)立于使用算法的客戶。
通俗來說
通過注入不同的類,改變業(yè)務(wù)方法的業(yè)務(wù)行為通過選擇策略類,來執(zhí)行不同算法分支。。此模式就是springIOC的思想。核心是通過注入對(duì)象,改變行為。
通過改變注入的對(duì)象,從而改變它的行為。
通過實(shí)例化不同的class對(duì)象,改變spring業(yè)務(wù)方法的業(yè)務(wù)行為。IOC思想的雛形。
角色:
- 上下文角色(Context):也叫Context封裝角色,起承上啟下的作用,屏蔽高層模塊對(duì)策略算法的直接訪問,封裝可能存在的變化。
- 抽象策略角色(AbstractStrategy):是對(duì)策略算法家族的抽象,通常為借口,定義每個(gè)策略算法必須具有的方法和屬性。
-
具體策略角色(ConcreteStrategy):用于實(shí)現(xiàn)抽象策略中的操作,即實(shí)現(xiàn)的具體算法。
image.png
context上下文:
public class Context {
Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
//上下文接口
public void contextInterface() {
strategy.algorithmInterface();
}
}
抽象策略角色:
public interface Strategy {
//算法方法
void algorithmInterface();
}
具體策略角色:
public class ConcreteStrategyA implements Strategy {
@Override
public void algorithmInterface() {
System.out.println("算法A實(shí)現(xiàn)");
}
}
調(diào)用代碼:
public class Client {
public static void main(String[] args) {
Context context;
context = new Context(new ConcreteStrategyA());
context.contextInterface();
context = new Context(new ConcreteStrategyB());
context.contextInterface();
context = new Context(new ConcreteStrategyC());
context.contextInterface();
}
}
優(yōu)點(diǎn):
- 算法可以自由切換。
- 避免使用多重條件轉(zhuǎn)移語(yǔ)句。
- 擴(kuò)展性好。
缺點(diǎn):
- 用戶需要知道所有策略類,并自行決定使用哪一種策略。
- 每個(gè)算法都會(huì)產(chǎn)生一個(gè)策略類,造成策略類過多。
應(yīng)用場(chǎng)景:
線程池的拒絕策略 默認(rèn)四種就是策略模式
java AWT中的layoutManager 布局管理器。
策略模式和裝飾模式的區(qū)別
策略模式側(cè)重于算法的自由替換,IOC的一種實(shí)現(xiàn)思想,關(guān)注的是方法級(jí)別的。
裝飾模式側(cè)重于對(duì)類的增強(qiáng),需要了解每一個(gè)裝飾器做什么功能,是關(guān)注類級(jí)別組合。
責(zé)任鏈模式(Chain of Responsibility)
定義
請(qǐng)求創(chuàng)建了一個(gè)接收者對(duì)象的鏈。這種模式給予請(qǐng)求的類型,對(duì)請(qǐng)求的發(fā)送者和接收者進(jìn)行解耦。這種類型的設(shè)計(jì)模式屬于行為型模式。
通俗來說
將請(qǐng)求傳給一個(gè)接收者鏈,由鏈將請(qǐng)求流轉(zhuǎn)給目標(biāo)對(duì)象。
當(dāng)某一個(gè)業(yè)務(wù)請(qǐng)求,多個(gè)對(duì)象需要對(duì)這個(gè)請(qǐng)求做處理,就可以使用責(zé)任鏈模式。把所有對(duì)象串成一個(gè)鏈?zhǔn)浇Y(jié)構(gòu),讓請(qǐng)求在鏈中流動(dòng)。
責(zé)任鏈和組合模式有點(diǎn)相似,組合模式是適合樹形結(jié)構(gòu)的業(yè)務(wù)邏輯,而鏈表呢是一種特殊的樹,只有一個(gè)后繼節(jié)點(diǎn)。責(zé)任鏈就是這種特殊的組合模式。
角色
抽象處理者角色(handler):定義出一個(gè)處理請(qǐng)求的接口,如果需要,接口也可以定義出一個(gè)模板方法以設(shè)定固定的流轉(zhuǎn)流程。這個(gè)角色通常是一個(gè)抽象類或者java接口。角色內(nèi)包含下個(gè)節(jié)點(diǎn)的引用。
具體處理者角色(ConcreteHandler):具體處理者,接到請(qǐng)求后,可以選擇將請(qǐng)求處理,或流轉(zhuǎn)到下個(gè)實(shí)例。

抽象處理者:
public abstract class Handler {
/**
* 持有后繼的責(zé)任對(duì)象
*/
protected Handler successor;
public Handler (Handler handler) {
this.successor = handler;
}
/**
* 示意處理請(qǐng)求的方法,雖然這個(gè)示意方法是沒有傳入?yún)?shù)的
* 但實(shí)際是可以傳入?yún)?shù)的,根據(jù)具體需要來選擇是否傳遞參數(shù)
*/
public abstract void handleRequest();
/**
* 取值方法
*/
public Handler getSuccessor() {
return successor;
}
/**
* 賦值方法,設(shè)置后繼的責(zé)任對(duì)象
*/
public void setSuccessor(Handler successor) {
this.successor = successor;
}
}
具體處理者:
public class ConcreteHandler extends Handler {
/**
* 處理方法,調(diào)用此方法處理請(qǐng)求
*/
@Override
public void handleRequest() {
/**
* 判斷是否有后繼的責(zé)任對(duì)象
* 如果有,就轉(zhuǎn)發(fā)請(qǐng)求給后繼的責(zé)任對(duì)象
* 如果沒有,則處理請(qǐng)求
*/
if(getSuccessor() != null)
{
System.out.println("放過請(qǐng)求");
getSuccessor().handleRequest();
}else
{
System.out.println("處理請(qǐng)求");
}
}
}
調(diào)用方:
public class Client {
public static void main(String[] args) {
//組裝責(zé)任鏈
Handler handler = new ConcreteHandler(null);
handler = new ConcreteHandler(handler);
//提交請(qǐng)求
handler1.handleRequest();
}
可以將流轉(zhuǎn)下一個(gè)流程的代碼抽象到父類中,由一個(gè)模板方法模式進(jìn)行封裝,所有子類流轉(zhuǎn)的代碼都是相同的。
if(null != getSuccessor()){
getSuccessor().handleRequest();
}
優(yōu)點(diǎn):
簡(jiǎn)化了調(diào)用,使用者不需要知道鏈的結(jié)構(gòu)
責(zé)任分離處理,讓各個(gè)節(jié)點(diǎn)各司其職。
動(dòng)態(tài)地增加減少責(zé)任鏈
應(yīng)用場(chǎng)景:
spring的攔截器 就是一個(gè)責(zé)任鏈模式
servlet的filter 就是一個(gè)責(zé)任鏈模式
springsecurity 的核心邏輯也是一個(gè)過濾器鏈。
做審批處理的時(shí)候。
命令模式(Command)
定義
將請(qǐng)求、命令封裝成對(duì)象,這樣可以讓項(xiàng)目使用這些對(duì)象來參數(shù)化其他對(duì)象。是的命令的請(qǐng)求者和執(zhí)行者解耦。
通俗來說
可以使發(fā)送者接收者解耦,發(fā)送者和接收者之間并沒有直接的引用關(guān)系。發(fā)送者只知道如何發(fā)送請(qǐng)求,不需要知道如何完成請(qǐng)求。
角色:
- 調(diào)用者(Invoker) :就是具體的調(diào)用者。
- 接收者(receiver) :如何實(shí)施和執(zhí)行一個(gè)請(qǐng)求相關(guān)的一個(gè)操作。
- 抽象命令角色(command):需要執(zhí)行的所有命令都在這里,可以使接口或抽象類
- 具體命令角色(ConcreteCommand):將一個(gè)接收者對(duì)象與一個(gè)命令綁定,調(diào)用接收者響應(yīng)的操作。

抽象命令
/**
* 命令抽象父類
*/
public abstract class Commond {
private Barbecue barbecue;
public Commond(Barbecue barbecue) {
super();
this.barbecue = barbecue;
}
public abstract void excuteCommond();
public Barbecue getBarbecue() {
return barbecue;
}
public void setBarbecue(Barbecue barbecue) {
this.barbecue = barbecue;
}
}
具體命令1
/**
* 烤羊腿命令
*/
public class MuttonCommod extends Commond{
public MuttonCommod(Barbecue barbecue) {
super(barbecue);
}
@Override
public void excuteCommond() {
super.getBarbecue().makeMutton();
}
}
具體命令2
/**
* 烤雞翅命令
*/
public class ChickenCommond extends Commond{
public ChickenCommond(Barbecue barbecue) {
super(barbecue);
}
@Override
public void excuteCommond() {
super.getBarbecue().makeChicken();
}
}
接收者(Receiver)
/**
* 燒烤師傅
*/
public class Barbecue {
public void makeMutton() {
System.out.println("烤羊肉串");
}
public void makeChicken() {
System.out.println("考雞肉串");
}
}
調(diào)用者(Invoker)
/**
* 服務(wù)員
*/
public class Waiter {
private List<Commond> commonds = new ArrayList<>();
public void addCommond(Commond commond) {
// TODO 可以做很多事情 記日志等等
commonds.add(commond);
}
public void removeCommond(Commond commond) {
// TODO 可以做很多事情 記日志等等
commonds.remove(commond);
}
public void Notify() {
for (Commond commond : commonds) {
commond.excuteCommond();
}
}
}
優(yōu)點(diǎn):
1.降低耦合。
2.容易擴(kuò)展新命令或者一組命令。
缺點(diǎn):
命令的無限擴(kuò)展會(huì)增加類的數(shù)量,提高了系統(tǒng)的復(fù)雜度。
命令模式和策略模式的區(qū)別:
- 命令模式和策略模式很相似,只是命令模式多了一個(gè)接收者的角色。
- 策略模式關(guān)注的是算法的替換問題,或者提供多種算法由調(diào)用者選擇使用。算法的自由替換是它的要點(diǎn)。
- 命令模式則是對(duì)調(diào)用方和接收方的解耦,要求把請(qǐng)求的內(nèi)容封裝成一個(gè)一個(gè)的命令,由接收者執(zhí)行。由于封裝成了命令就可以對(duì)命令進(jìn)行多種處理,撤銷 排隊(duì)等等。
- 策略模式適用于算法要求變換的場(chǎng)景。命令模式適合兩個(gè)緊耦合關(guān)系的對(duì)象解耦 或者多命令多撤銷的場(chǎng)景。
舉兩個(gè)具體的例子:
1.如果要實(shí)現(xiàn)壓縮 我們選擇zip算法 和gzip算法 那么策略模式就會(huì)設(shè)計(jì)成兩個(gè)策略:zip 一個(gè)策略 里面包含了 壓縮和解壓縮的方法,gzip一個(gè)策略 也包含一個(gè)壓縮和一個(gè)解壓縮的算法。
而命令模式就會(huì)設(shè)計(jì)成4個(gè)命令 zip壓縮命令、zip解壓縮命令、gzip壓縮命令、gzip解壓縮命令。
由此看出命令模式關(guān)注的是通過封裝成命令來解耦。
2.另外一種情況說明命令模式可以像適配器模式一樣,作為一種補(bǔ)救辦法。假設(shè)有A、B、C三個(gè)類,三個(gè)類的功能大體相似。但沒有實(shí)現(xiàn)同一接口,這個(gè)時(shí)候我們可以用命令模式解除他們與調(diào)用者之間的緊耦合。封裝三個(gè)命令實(shí)現(xiàn)類,來聚合A、B、C三個(gè)類,通過命令的方式讓調(diào)用者不直接操作A、B、C三個(gè)類。而此時(shí)如果是想用策略模式去實(shí)現(xiàn),需要修改大量的代碼 讓A、B、C去實(shí)現(xiàn)相同的接口,并不適用。
命令模式和外觀模式的區(qū)別:
- 1.外觀模式是結(jié)構(gòu)型模型,命令模式是行為型模型。
- 2.外觀模式關(guān)注類如何組合聚合來解耦,命令模式關(guān)注方法間的通信來解耦。
- 3.外觀模式主要為了簡(jiǎn)化操作。封裝內(nèi)部,不符合開閉原則,新增需要修改外觀類。命令模式主要為了發(fā)送方接收方解耦,符合開閉原則
- 4.控制粒度不一樣。還是用遙控器來舉例,命令模式像一個(gè)多按鈕的遙控器,每個(gè)按鈕是一個(gè)命令,調(diào)用方使用某些按鈕進(jìn)行開關(guān)操作,并且還支持等待執(zhí)行的行為。命令模式能細(xì)粒度的控制每個(gè)按鈕的執(zhí)行順序,執(zhí)行是否撤回。而外觀模式則是更像遙控器上只有一個(gè)按鈕,而這一個(gè)按鈕執(zhí)行多個(gè)設(shè)定好順序的開關(guān)操作。并且中間不能撤回。外觀模式無法穿透外觀類(或接口)去細(xì)粒度的控制內(nèi)部的執(zhí)行順序或某些特殊的邏輯。
命令模式控制內(nèi)部細(xì)節(jié)操作,外觀模式控制整體操作。
應(yīng)用場(chǎng)景:
- 1.請(qǐng)求的調(diào)用者和接收者,需要解耦。使得調(diào)用者和接收者不直接交互。
- 2.需要抽象出等待執(zhí)行的行為。
- 3.命令模式適合做可撤銷類的應(yīng)用。
- 對(duì)沒有使用面向接口編程的時(shí)候,就可以從中間加入一個(gè)command命令抽象層,用命令模式來改造調(diào)用方式。來解耦。
解釋器模式(Interpreter)
定義
給定一個(gè)語(yǔ)言,定義它的文法的一種表示,并定義一個(gè)解釋器,這個(gè)解釋器使用該表示來解釋語(yǔ)言中的句子。
通俗來說
對(duì)于一些固定文法構(gòu)建一個(gè)解釋句子的解釋器。
角色:
- 抽象表達(dá)式(Expression):抽象表達(dá)式,定義解釋器的接口,約定解釋器的解釋操作,主要包含一個(gè)interpret()方法。
- 終結(jié)表達(dá)式(TerminalExpression):是抽象表達(dá)式的子類,用來實(shí)現(xiàn)文法中與終結(jié)符相關(guān)的操作,文法中的每一個(gè)終結(jié)符都有一個(gè)具體終結(jié)表達(dá)式與之相對(duì)應(yīng)。
- 非終結(jié)表達(dá)式(NonTerminalExpression):也是抽象表達(dá)式的子類,用來實(shí)現(xiàn)文法中非終結(jié)符的相關(guān)操作,文法中每條規(guī)則都對(duì)應(yīng)一個(gè)非終結(jié)符表達(dá)式。
-
上下文容器(Context):context是環(huán)境角色,包含解釋器之外的一些全局信息
解釋器模式類圖
通過解釋器實(shí)現(xiàn)四則運(yùn)算,如計(jì)算加減法表達(dá)式,UML如圖:

上下文容器context
public class Context{
//定義表達(dá)式
private Expression expression;
//構(gòu)造函數(shù)傳參,并解析
public Context(String expStr) {
//安排運(yùn)算先后順序
Stack<Expression> stack = new Stack<>();
//表達(dá)式拆分為字符數(shù)組
char[] charArray = expStr.toCharArray();
Expression left = null;
Expression right = null;
for(int i=0; i<charArray.length; i++) {
switch (charArray[i]) {
case '+': //加法
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new AddExpression(left, right));
break;
case '-': //減法
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left, right));
break;
default: //公式中的變量
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
this.expression = stack.pop();
}
//計(jì)算
public int run(HashMap<String, Integer> var) {
return this.expression.interpreter(var);
}
}
抽象表達(dá)式
public abstract class Expression {
//解析公式和數(shù)值,key是公式中的參數(shù),value是具體的數(shù)值
public abstract int interpreter(HashMap<String,Integer> varMap)
}
非終結(jié)解釋器(變量解釋器)
public class VarExpression extends Expression {
private String key;
public VarExpression(String key) {
this.key = key;
}
@Override
public int interpreter(HashMap<String, Integer> var) {
return var.get(this.key);
}
}
終結(jié)解釋器(抽象運(yùn)算符解釋器)
public class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpreter(HashMap<String, Integer> var) {
// TODO Auto-generated method stub
return 0;
}
}
終結(jié)解釋器實(shí)現(xiàn)1 (加法解釋器)
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
終結(jié)解釋器實(shí)現(xiàn)2(減法解釋器)
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
調(diào)用方
public class Client {
public static void main(String[] args) throws IOException {
String expStr = getExpStr();
HashMap<String, Integer> var = getValue(expStr);
Context context = new Context (expStr);
System.out.println("運(yùn)算結(jié)果:" + expStr + "=" + calculator.run(var));
}
//獲得表達(dá)式
public static String getExpStr() throws IOException {
System.out.print("請(qǐng)輸入表達(dá)式:");
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
}
//獲得值映射
public static HashMap<String, Integer> getValue(String expStr) throws IOException {
HashMap<String, Integer> map = new HashMap<>();
for(char ch : expStr.toCharArray()) {
if(ch != '+' && ch != '-' ) {
if(! map.containsKey(String.valueOf(ch))) {
System.out.print("請(qǐng)輸入" + String.valueOf(ch) + "的值:");
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
map.put(String.valueOf(ch), Integer.valueOf(in));
}
}
}
return map;
}
}
優(yōu)點(diǎn):
解決特定重復(fù)出現(xiàn)的場(chǎng)景??蓴U(kuò)展性好
缺點(diǎn):
1.解釋器模式會(huì)引起類膨脹
2.解釋器模式采用遞歸調(diào)用方法,導(dǎo)致調(diào)試復(fù)雜。
3.使用大量循環(huán)遞歸,效率是一個(gè)不容忽視的問題。
盡量不要在重要的模塊中使用解釋器模式,否則維護(hù)是一個(gè)很大的問題。
應(yīng)用場(chǎng)景:
1.正則表達(dá)式
2.Spring el表達(dá)式
3.corn表達(dá)式
迭代器模式(Iterator)
定義
提供一種遍歷集合元素的統(tǒng)一接口,用一致的方法遍歷集合元素,不需要知道集合對(duì)象的底層表示,即不暴露其內(nèi)部結(jié)構(gòu)。
通俗來說
用戶通過特定的接口訪問容器的數(shù)據(jù),不需要了解容器內(nèi)部的數(shù)據(jù)結(jié)構(gòu)。
角色:
- 抽象迭代器(Iterator):迭代器接口,提供hasNext、next、remove方法。
- 具體迭代器(ConcreteItorator):具體的迭代器類,管理迭代
- 聚合接口(aggregate):一個(gè)統(tǒng)一的聚合接口,將調(diào)用方和具體的聚合解耦。包含一個(gè)createIterator方法。
- 具體聚合(ConcreteAggregate):持有對(duì)象的集合,并提供createIterator的具體方法。
-
element(元素)
迭代器類圖
聚合接口
public interface Aggregate{
public abstract Iterator createIterator();
}
迭代器接口
public interface Iterator<T>{
boolean hasNext();
T next();
void remove();
}
元素
public class Book{
private String name ;
public Book(String name){
this.name=name;
}
public String getName(){
return name;
}
}
具體聚合
public class BookShelf implements Aggregate {
private List<Book> books;
public BookShelf() {
this.books = new ArrayList<Book>();
}
public Book getBookAt(int index) {
return books.get(index);
}
public void appendBook(Book book) {
books.add(book);
}
public int getLength() {
return books.size();
}
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
具體迭代器
public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
public boolean hasNext() {
if (index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
優(yōu)點(diǎn):
1.面向接口編程
2.將元素的遍歷和實(shí)現(xiàn)分離開來
為什么不讓容器直接繼承Iterator接口,Iterable接口會(huì)不會(huì)有一種多此一舉的感覺?
個(gè)人認(rèn)為是為了讓數(shù)據(jù)處理邏輯和容器內(nèi)部數(shù)據(jù)管理進(jìn)行分離,提供容器數(shù)據(jù)的安全性。
應(yīng)用場(chǎng)景:
需要遍歷的場(chǎng)景,提供多種遍歷。而無需暴露其內(nèi)部結(jié)構(gòu)。
JDK中 Collection包下基本都實(shí)現(xiàn)了iterator
- fail-fast:直接在容器上進(jìn)行遍歷,在遍歷過程中一點(diǎn)發(fā)現(xiàn)容器被修改,會(huì)拋出ConcurrentModificationException異常導(dǎo)致遍歷失敗,常見的hashMap arrayList
- fail-safe:遍歷時(shí)復(fù)制一份容器,對(duì)容器修改不影響遍歷。ConcurrentHaspMap和CopyOnWrite容器
中介者模式(Mediator)
定義
定義一個(gè)中介者對(duì)象, 封裝一系列對(duì)象的交互關(guān)系, 使得各對(duì)象不必顯示的相互引用, 從而使其耦合松散, 而且可以獨(dú)立的改變它們的交互
通俗來說
多個(gè)類相互耦合,會(huì)形成網(wǎng)狀結(jié)構(gòu),使用中介者模式將網(wǎng)站結(jié)構(gòu)分離為星狀結(jié)構(gòu),由中介者來控制這些類之間消息的相互流轉(zhuǎn),從而使其耦合松散。
角色
1.中介者(Mediator):抽象中介者,用于定義統(tǒng)一接口,用于各同事之間的交互。
2.具體中介者(ConcreteMediator):具體中介者持有各同事對(duì)象的引用,負(fù)責(zé)協(xié)調(diào)各同事對(duì)象的行為以完成協(xié)作,因此具體中介者必須依賴具體同事對(duì)象。(不符合依賴倒置原則)
-
3.同事角色(Colleague):每個(gè)同事角色都知道中介者對(duì)象,而且與其他同事角色通信的時(shí)候,都通過中介者協(xié)作完成。每個(gè)同事角色都有兩種行為:
- 1.自發(fā)行為:同事本身的行為,如修改自身的狀態(tài),與其他同事或者中介者沒有任何聯(lián)系。
- 2.依賴方法:必須依賴中介者才能完成的行為。
4.具體同事角色(ConcreteColleague): 實(shí)現(xiàn)抽象同事角色。

中介者
public interface Mediator {
/**
* 注冊(cè)同事方法 可以使用構(gòu)造器 而不用此方法
*/
void register(Member member);
/**
* 發(fā)出命令方法
*/
void notifyOthers(String info,Member member);
具體中介者
public class ConcreteMediator implements Mediator {
private HashMap memberMap = new HashMap<String, Member >();
public void register( Member member) {
memberMap.put(member.getClass().getSimpleName(), member);
}
public void notifyOthers(String info, Member member){
String name = member.getClass().getSimpleName();
switch (name){
case "Member01": {
memberMap.get("Member02").recv(info);
memberMap.get("Member03").recv(info);
break;
}
case "Member02": {
memberMap.get("Member01").recv(info);
memberMap.get("Member03").recv(info);
break;
}
case "Member03": {
memberMap.get("Member01").recv(info);
memberMap.get("Member02").recv(info);
break;
}
}
}
}
同事角色
public abstract class Member {
protected Mediator mediator;
public Mediator getMediator() {
return mediator;
}
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
//依賴方法 將調(diào)用行為傳遞給中介者,讓中介者具體進(jìn)行下一步操作。 也可以將此方法延遲到子類實(shí)現(xiàn)
public void send(String info){
mediator.notifyOthers(info,this);
}
//自發(fā)行為---中介者調(diào)用
public abstract void recv(String info);
}
具體同事角色1
public class Member01 extends Member{
public void recv(String info){
System.out.println(this.getClass().getName() + " 收到消息: " + info +"進(jìn)行處理1");
}
}
具體同事角色2
public class Member02 extends Member{
public void recv(String info){
System.out.println(this.getClass().getName() + " 收到消息: " + info +"進(jìn)行處理2");
}
}
具體同事角色3
public class Member03 extends Member{
public void recv(String info){
System.out.println(this.getClass().getName() + " 收到消息: " + info +"進(jìn)行處理3");
}
}
調(diào)用方
public class Client {
public static void main(String[] args) {
Member member01 = new Member01();
Member member02 = new Member02();
Member member03 = new Member03();
Mediator mediator = new Mediator();
mediator.register(member01);
mediator.register(member01);
mediator.register(member01);
member01.setMediator(mediator);
member02.setMediator(mediator);
member03.setMediator(mediator);
member01.notifyOthers("消息1");
member02.notifyOthers("消息2");
member03.notifyOthers("消息3");
}
}
優(yōu)點(diǎn):
1.多個(gè)類相互耦合,會(huì)形成網(wǎng)狀結(jié)構(gòu),使用中介者模式將網(wǎng)站結(jié)構(gòu)分離為星狀結(jié)構(gòu),進(jìn)行解耦。
2.減少類間的依賴,降低耦合,符合迪米特法則。
缺點(diǎn):
中介者是將各個(gè)類中的耦合,轉(zhuǎn)移到自己的內(nèi)部,中介者承擔(dān)了更多的責(zé)任,一個(gè)中介者出現(xiàn)問題,整個(gè)系統(tǒng)就會(huì)收到影響。
如果設(shè)計(jì)不當(dāng),中介者對(duì)象本身會(huì)過于復(fù)雜。 本身并不符合開閉原則。
應(yīng)用場(chǎng)景:
將網(wǎng)狀依賴關(guān)系轉(zhuǎn)化為星狀依賴關(guān)系。
中介者模式、代理模式、外觀模式區(qū)別
外觀模式(Facade Pattern):定義一個(gè)外觀類,隱藏系統(tǒng)的復(fù)雜性,為客戶端提供簡(jiǎn)化的方法和對(duì)現(xiàn)有系統(tǒng)類方法的委托調(diào)用。
代理模式(Proxy Pattren):用一個(gè)代理類代表另一個(gè)類的功能,但是不改變被代理類的功能。目的是控制對(duì)被代理類的訪問。
中介者模式(Mediator Pattern):各個(gè)類之間互相交互,形成網(wǎng)狀結(jié)構(gòu),中介者使各對(duì)象之間不直接相互引用。從而耦合松散。從而形成網(wǎng)狀結(jié)構(gòu)。例如MVC中的C 就是view和model的中介者。讓view與model 不網(wǎng)狀交互。
觀察者模式(Observer)
定義
當(dāng)對(duì)象間存在一對(duì)多關(guān)系時(shí),則使用觀察者模式(Observer Pattern)。比如,當(dāng)一個(gè)對(duì)象被修改時(shí),則會(huì)自動(dòng)通知它的依賴對(duì)象。觀察者模式屬于行為型模式。
通俗來說
又被稱為發(fā)布-訂閱模式 一個(gè)對(duì)象狀態(tài)改變時(shí),通知其他對(duì)象。為的是盡量弱化對(duì)象間的依賴。
角色
抽象主題(Subject):也叫抽象目標(biāo)類,提供了一個(gè)用于保存觀察者對(duì)象的集合,和增加刪除,以及通知觀察者的方法。
具體主題(ConcreteSubject):實(shí)現(xiàn)通知方法的具體類。
抽象觀察者(Observer):是一個(gè)抽象類或接口,包含一個(gè)update方法。當(dāng)接到主題更改通知時(shí)被調(diào)用。
具體觀察者(ConcreteOberser):抽象觀察者的實(shí)現(xiàn)。

抽象主題(Subject)
public class Subject {
//觀察者數(shù)組
private Vector<Observer> oVector = new Vector<>();
//增加一個(gè)觀察者
public void addObserver(Observer observer) {
this.oVector.add(observer);
}
//刪除一個(gè)觀察者
public void deleteObserver(Observer observer) {
this.oVector.remove(observer);
}
//通知所有觀察者
public void notifyObserver() {
for(Observer observer : this.oVector) {
observer.update();
}
}
}
具體主題(ConcreteSubject)
public class ConcreteSubject extend Subject {
public void doSomething() {
//...
super.notifyObserver();
}
}
抽象觀察者(Observer)
public interface Observer {
void update();
}
具體觀察者(ConcreteObserver)
public class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("收到消息,進(jìn)行處理");
}
}
調(diào)用方
public class Client {
public static void main(String[] args) {
//創(chuàng)建一個(gè)主題
ConcreteSubject subject = new ConcreteSubject();
//定義一個(gè)觀察者
Observer observer = new ConcreteObserver();
//觀察
subject.addObserver(observer);
//開始活動(dòng)
subject.doSomething();
}
}
優(yōu)點(diǎn):
1.實(shí)現(xiàn)了觸發(fā)機(jī)制。
2.觀察者和被觀察者是弱耦合。
觀察者模式和消息中間件這種發(fā)布訂閱模式區(qū)別
發(fā)布訂閱是 通知到broker的topic中 broker再通知訂閱方。中間是有數(shù)據(jù)傳輸。沒有耦合。而觀察者模式是有耦合的。
發(fā)布訂閱更像是觀察者模式+命令模式,觀察者負(fù)責(zé)事件觸發(fā),有消息了觀察者處理觸發(fā),命令模式負(fù)責(zé)解耦發(fā)送方接收方,傳輸?shù)臄?shù)據(jù)其實(shí)就是命令。發(fā)送方并不知道具體會(huì)由哪個(gè)接收方處理。
應(yīng)用場(chǎng)景:
事件的觸發(fā)場(chǎng)景
發(fā)布訂閱這種,跨系統(tǒng)消息變換場(chǎng)景
JDK對(duì)觀察者模式的支持
Observer接口 觀察者接口 觀察者需要實(shí)現(xiàn)的接口
Observable類 是觀察的目標(biāo)發(fā)布方
備忘錄模式(Memento)
定義
在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),這樣以后就可以將該對(duì)象恢復(fù)到原先保存的狀態(tài)。
通俗來說
給用戶提供一種可以恢復(fù)狀態(tài)的機(jī)制,可以使用戶能夠比較方便地回到某個(gè)歷史狀態(tài)。
角色
- 原發(fā)器(originator):是一個(gè)普通的類,創(chuàng)建備忘錄的原始角色,備忘錄存儲(chǔ)它的當(dāng)前內(nèi)部狀態(tài),也可以使用備忘錄來恢復(fù)其內(nèi)部狀態(tài)。
- 備忘錄(Memento):存儲(chǔ)原發(fā)器的內(nèi)部狀態(tài),根據(jù)原發(fā)器來決定保存哪些內(nèi)部狀態(tài)備忘錄一般可以參考原發(fā)器設(shè)計(jì),根據(jù)實(shí)際需要確定備忘錄中的屬性。需要注意的是,除了原發(fā)器和守護(hù)者類之外,備忘錄對(duì)象不能直接供其他類使用。
- 守護(hù)者(CareTaker):守護(hù)者又稱(管理者,負(fù)責(zé)人)。他負(fù)責(zé)保存?zhèn)渫?,但是不能?duì)備忘錄的內(nèi)容進(jìn)行操作或檢查。在守護(hù)者勒種,可以存儲(chǔ)多個(gè)備忘錄對(duì)象。但是他只負(fù)責(zé)存儲(chǔ),而不能修改對(duì)象,也無需知道對(duì)象的實(shí)現(xiàn)細(xì)節(jié)。

原發(fā)器
public class Originator {
private String state;
public void setState(String state){
this.state = state;
}
public String getState(){
return state;
}
public Memento saveStateToMemento(){
return new Memento(state);
}
public void getStateFromMemento(Memento Memento){
state = Memento.getState();
}
}
備忘錄
public class Memento {
private String state;
public Memento(String state){
this.state = state;
}
public String getState(){
return state;
}
}
守護(hù)者
public class CareTaker {
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento state){
mementoList.add(state);
}
public Memento get(int index){
return mementoList.get(index);
}
}
調(diào)用方
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
CareTaker careTaker = new CareTaker();
originator.setState("State #1");
originator.setState("State #2");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #3");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #4");
System.out.println("Current State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(0));
System.out.println("First saved State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(1));
System.out.println("Second saved State: " + originator.getState());
}
}
優(yōu)點(diǎn):
1.提供了狀態(tài)恢復(fù)機(jī)制。
2.提供了封裝,用戶不需要關(guān)心狀態(tài)的保存細(xì)節(jié)。
缺點(diǎn):
如果成員變量過多,勢(shì)必會(huì)占用比較大的資源,而且每次保存都會(huì)消耗一定的內(nèi)存。
應(yīng)用場(chǎng)景:
- 1.瀏覽器中的后退
- 2.springSecurity 登錄后跳轉(zhuǎn)到之前訪問的頁(yè)
- 3.打游戲時(shí)的存檔
- 4.windows的ctrl+z
- 5.數(shù)據(jù)庫(kù)里的事務(wù)管理。
一般備忘錄模式和原型模式配合使用。
狀態(tài)模式(State)
定義
當(dāng)一個(gè)對(duì)象的內(nèi)在狀態(tài)改變時(shí)允許改變其行為,這個(gè)對(duì)象看起來像是改變了其類。
通俗來說
狀態(tài)模式很像策略模式,但是關(guān)注于狀態(tài)的轉(zhuǎn)換而改變內(nèi)在行為,而且狀態(tài)之間相互有依賴。
狀態(tài)模式也能減少代碼中的if else 判斷不同的狀態(tài)
角色
- 抽象狀態(tài)(State):定義一個(gè)接口,來封裝使用Context的一個(gè)特定狀態(tài)相關(guān)的行為。
- 具體狀態(tài)(ConcreteState):抽象狀態(tài)的實(shí)現(xiàn)。
-
上下文環(huán)境(Context):定義了客戶程序需要的接口,并且維護(hù)一個(gè)狀態(tài)角色的實(shí)例,將與狀態(tài)相關(guān)的操作委托給狀態(tài)角色實(shí)例來處理。
狀態(tài)模式類圖
抽象狀態(tài)
public abstract class AbstractState {
/** 封裝了上下文 */
protected Context context;
public void setContext(Context context){
this.context = context;
}
/** 執(zhí)行方法 */
abstract void run();
/** 切換至下一個(gè)狀態(tài) */
abstract void next();
}
具體狀態(tài)1
public class AState extends AbstractState {
/** 執(zhí)行方法 */
@Override
public void run() {
System.out.println("執(zhí)行 AState 的 run() 方法");
}
/** 切換至下一個(gè)狀態(tài) */
@Override
void next() {
System.out.println("執(zhí)行 AState 的 next() 方法");
// 定義了下一個(gè)狀態(tài)是 BState
context.setState(new BState());
}
}
具體狀態(tài)2
public class BState extends AbstractState {
/** 執(zhí)行方法 */
@Override
public void run() {
System.out.println("執(zhí)行 BState 的 run() 方法");
}
/** 切換至下一個(gè)狀態(tài) */
@Override
void next() {
System.out.println("執(zhí)行 BState 的 next() 方法");
// 定義了下一個(gè)狀態(tài)是 CState
context.setState(new CState());
}
}
具體狀態(tài)3
public class CState extends AbstractState {
/** 執(zhí)行方法 */
@Override
public void run() {
System.out.println("執(zhí)行 CState 的 run() 方法");
}
/** 切換至下一個(gè)狀態(tài) */
@Override
void next() {
System.out.println("執(zhí)行 CState 的 next() 方法");
System.out.println("已經(jīng)是終態(tài)了!");
}
}
上下文環(huán)境
public class Context {
private AbstractState state;
public AbstractState getState() {
return state;
}
/** 設(shè)置當(dāng)前狀態(tài) */
public void setState(AbstractState state) {
this.state = state;
// 記得 setContext,不然會(huì)空指針
this.state.setContext(this);
}
/** 執(zhí)行方法 */
public void run(){
this.state.run();
}
/** 下一個(gè)狀態(tài) */
public void next(){
this.state.next();
}
}
調(diào)用方
public class TestMe {
public static void main(String[] args){
Context context = new Context();
context.setState(new AState());
context.run();
System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());
context.next();
System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());
context.run();
System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());
context.next();
System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());
context.run();
System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());
context.next();
System.out.println("當(dāng)前狀態(tài):" + context.getState().toString());
}
}
優(yōu)點(diǎn)
1.將狀態(tài)相關(guān)的行為封裝在一個(gè)狀態(tài)中,符合單一職責(zé)。
缺點(diǎn)
狀態(tài)較多的情況下,導(dǎo)致類膨脹。
結(jié)構(gòu)和實(shí)現(xiàn)都比較復(fù)雜,使用不當(dāng)造成代碼結(jié)構(gòu)混亂。
并不符合開閉原則,新增狀態(tài),變化到這個(gè)狀態(tài),需要修改與之關(guān)聯(lián)的狀態(tài)
應(yīng)用場(chǎng)景
1.當(dāng)一個(gè)對(duì)象的行為取決于它的狀態(tài),并且在運(yùn)行時(shí)需要根據(jù)狀態(tài)做出不同行為,可以使用狀態(tài)模式。
2.一個(gè)操作中含有大量的if else 并且判斷條件是狀態(tài)的時(shí)候。
狀態(tài)模式和策略模式的區(qū)別
- 策略模式的客戶端必須對(duì)所有策略類了解,權(quán)衡場(chǎng)景策略選擇。對(duì)客戶暴露。算法之間沒有依賴關(guān)系。策略模式不依賴上下文。
- 狀態(tài)模式依賴于狀態(tài)變化時(shí),其內(nèi)部行為變化。將動(dòng)作委托到代表當(dāng)前狀態(tài)的對(duì)象,對(duì)外表現(xiàn)為類發(fā)生了變化。狀態(tài)之間有依賴關(guān)系。狀態(tài)模式需要依賴上下文容器。
訪問者模式(Visitor)
設(shè)計(jì)模式中比較復(fù)雜的。
什么是雙重分派?
http://www.itdecent.cn/p/a42928e3f9d3
1.首先在客戶端程序中,將 具體的Visit訪問者為參數(shù)傳遞到了Element中(第一次分派)
2.然后element實(shí)現(xiàn)類 調(diào)用作為參數(shù)的visit中的具體方法,將自己(this)作為參數(shù)傳入,完成第二次分派。
定義
封裝一些作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作,它可以在不改變這個(gè)數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些元素的新的操作。
通俗來說
利用雙重分派機(jī)制,Element接口需要accept方法來接受一個(gè)訪問者,而accept方法中又調(diào)用visit 中的具體方法,傳入element自己(this) 一共這兩次分派,來實(shí)現(xiàn)將數(shù)據(jù)元素和數(shù)據(jù)操作分離。適合對(duì)象結(jié)構(gòu)不經(jīng)常變化的情況。
角色
- 抽象訪問者(Visitor):是抽象訪問者,為每一個(gè)ConcreteElement的每一個(gè)類聲明一個(gè)visit操作。
- 具體訪問者(ConcreteVisitor):是一個(gè)具體的訪問者,實(shí)現(xiàn)每個(gè)有Visitor聲明的操作。是每個(gè)操作的實(shí)現(xiàn)部分。
- 抽象元素(Element):定義一個(gè)accept方法,接收一個(gè)訪問者對(duì)象。
- 具體元素(ConcreteElement): 實(shí)現(xiàn)了accept方法。
- 對(duì)象結(jié)構(gòu)(ObjectStructure):

抽象訪問者
interface Visitor {
void visit(Games games);
void visit(Photos photos);
}
抽象元素
interface Computer {
void accept(Visitor visitor);
}
具體訪問者
class ZhangSan implements Visitor {
@Override
public void visit(Games games) {
games.play();
}
@Override
public void visit(Photos photos) {
photos.watch();
}
}
class LiSi implements Visitor {
@Override
public void visit(Games games) {
games.play();
}
@Override
public void visit(Photos photos) {
photos.watch();
}
}
具體元素
class Games implements Computer {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void play() {
System.out.println("play lol");
}
}
class Photos implements Computer {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void watch() {
System.out.println("watch scenery photo");
}
}
對(duì)象結(jié)構(gòu)
class ObjectStructure {
private List<Computer> computers = new ArrayList<Computer>();
public void action(Visitor visitor) {
computers.forEach(c -> {
c.accept(visitor);
});
}
public void add(Computer computer) {
computers.add(computer);
}
}
調(diào)用方
public static void main(String[] args) {
// 創(chuàng)建一個(gè)結(jié)構(gòu)對(duì)象
ObjectStructure os = new ObjectStructure();
// 給結(jié)構(gòu)增加一個(gè)節(jié)點(diǎn)
os.add(new Games());
// 給結(jié)構(gòu)增加一個(gè)節(jié)點(diǎn)
os.add(new Photos());
// 創(chuàng)建一個(gè)訪問者
Visitor visitor = new ZhangSan();
os.action(visitor);
}
優(yōu)點(diǎn)
1.數(shù)據(jù)元素和數(shù)據(jù)操作解耦,使得操作集合可以獨(dú)立變化。擴(kuò)展性好。
2.符合單一職責(zé)
缺點(diǎn)
1.不適合對(duì)象結(jié)構(gòu)經(jīng)常變化的情況元素?cái)U(kuò)展比較困難,需要修改visitor以及visitor所有的實(shí)現(xiàn)類。
2.元素對(duì)訪問者公布細(xì)節(jié),違反了迪米特法則。
3.依賴具體類,而沒有依賴接口,違反了依賴倒置原則。
應(yīng)用場(chǎng)景
數(shù)據(jù)結(jié)構(gòu)穩(wěn)定,作用于數(shù)據(jù)結(jié)構(gòu)的操作經(jīng)常變化。
讓靜態(tài)分派轉(zhuǎn)為動(dòng)態(tài)分派的情況。
在遍歷集合的過程中對(duì)元素進(jìn)行固定的處理是常有的需求。Visitor模式正是為了應(yīng)對(duì)這種需求而出現(xiàn)的。在訪問元素集合的過程中對(duì)元素進(jìn)行相同的處理,這種模式就是Vistor模式。



