前言
推薦看這篇文章之前先了解Java日記之設(shè)計(jì)模式初探勒叠。
行為型設(shè)計(jì)模式總共有11種
1.模板方法模式
2.迭代器模式
3.策略模式
4.解釋器模式
5.觀察者模式
6.備忘錄模式
7.命令模式
8.中介者模式
9.責(zé)任鏈模式
10.訪問者模式
11.狀態(tài)模式
1.模板方法模式
定義:定義了一個(gè)算法的骨架式矫,并允許子類為一個(gè)或多個(gè)步驟提供實(shí)現(xiàn)加勤。比如把大象放進(jìn)冰箱分幾步,首先打開開門,然后放進(jìn)去,最后關(guān)山門,這3個(gè)步驟就可以用模板方法設(shè)計(jì)模式汇陆。這個(gè)算法就是一個(gè)處理過程。模板方法使得子類可以在不改變算法結(jié)構(gòu)的情況下带饱,重新定義算法的某些步驟毡代。
適用場景:一次性實(shí)現(xiàn)一個(gè)算法的不變的部分,并將可變的行為留給子類來實(shí)現(xiàn)勺疼。各子類中公共的行為被提取出來并集中到一個(gè)公共父類中教寂,從而避免代碼重復(fù)。
優(yōu)點(diǎn):提高復(fù)用性执庐,提高擴(kuò)展性酪耕。符合開閉原則。
缺點(diǎn):類數(shù)目增加轨淌,增加了系統(tǒng)實(shí)現(xiàn)的復(fù)雜度迂烁,繼承關(guān)系自身缺點(diǎn),如果父類添加新的抽象方法递鹉,所有子類都要修改一遍盟步。
擴(kuò)展:鉤子方法(它是這個(gè)模板對子類更進(jìn)一層的開發(fā)和拓展)。
相關(guān)設(shè)計(jì)模式:工廠方法模式(工廠是模板的一種特殊實(shí)現(xiàn))躏结。策略模式(都有封裝算法却盘,策略模式的目的是使不同的算法可以相互替換,并且不影響應(yīng)用層的使用媳拴。模板是針對一個(gè)定義算法的流程谷炸,而將一些不太一樣的具體實(shí)現(xiàn)步驟交給子類實(shí)現(xiàn),模板是不改變算法的流程的禀挫,策略是可以改變的,并且是可以相互替換的)拓颓。
代碼舉例:
我們引入一個(gè)場景语婴,比如說制作一門課程。對于制作一個(gè)課程驶睦,需要制作PPT砰左,制作視頻,還要決定是否要寫一個(gè)手記场航,還有是否要提供一些素材缠导,意思就是說,不同的課程溉痢,制作的主線是一樣的僻造,在細(xì)分的一些細(xì)節(jié)上還是有所不同憋他。比如有一些課程不需要手記,所以寫手機(jī)這個(gè)是一個(gè)非必選項(xiàng)髓削,完全留給子類來擴(kuò)展竹挡,那就可以把這個(gè)方法做出鉤子方法。
//定義模板標(biāo)準(zhǔn)
public abstract class ACourse {
protected final void makeCourse(){
this.makePPT();
this.makeVideo();
if (needWriteArticle()){
this.writeArticle();
}
this.packageCourse();
}
//制作什么課程都要PPT
final void makePPT(){
System.out.println("制作PPT");
}
final void makeVideo(){
System.out.println("制作視頻");
}
final void writeArticle(){
System.out.println("編寫手記");
}
//鉤子方法
protected boolean needWriteArticle(){
return false;
}
abstract void packageCourse();
}
//設(shè)計(jì)模式課程
public class DesignPatternCourse extends ACourse{
@Override
void packageCourse() {
System.out.println("提供課程Java源代碼");
}
//如果這個(gè)課程需要手記就重寫為true
@Override
protected boolean needWriteArticle() {
return true;
}
}
//前端課程
public class FECourse extends ACourse{
//這個(gè)鉤子值也可以交給應(yīng)用層
private boolean needWriteArticleFlag = false;
public FECourse(boolean needWriteArticleFlag) {
this.needWriteArticleFlag = needWriteArticleFlag;
}
@Override
void packageCourse() {
System.out.println("提供課程前端代碼");
System.out.println("提供課程圖片素材");
}
@Override
protected boolean needWriteArticle() {
return needWriteArticleFlag;
}
}
//測試類
public class Test {
public static void main(String[] args) {
System.out.println("后端設(shè)計(jì)模式課程start---");
ACourse designCourse = new DesignPatternCourse();
designCourse.makeCourse();
System.out.println("后端設(shè)計(jì)模式課程end---");
System.out.println("前端課程start---");
ACourse fECourse = new FECourse(true);
fECourse.makeCourse();
System.out.println("前端課程end---");
}
}
通過定義一個(gè)模板類立膛,然后由子類詳細(xì)的編寫具體的步驟揪罕,模板方法設(shè)計(jì)模式的核心就是要加final,否則子類是可以重寫的宝泵,這就是失去模板方法設(shè)計(jì)模式的用意了好啰。
2.迭代器模式
定義:提供一種方法,順序訪問一個(gè)集合對象中的各個(gè)元素儿奶,而又不暴露該對象的內(nèi)部表示框往。簡單理解就是遍歷。
適用場景:訪問一個(gè)集合對象的內(nèi)容而無需暴露它的內(nèi)部表示廓握。為遍歷不同的集合結(jié)構(gòu)提供一個(gè)統(tǒng)一的接口搅窿。
優(yōu)點(diǎn):分離了集合對象的遍歷行為。
缺點(diǎn):類的個(gè)數(shù)成對增加隙券。因?yàn)檫@個(gè)設(shè)計(jì)模式是把存儲(chǔ)數(shù)據(jù)和遍歷數(shù)據(jù)這兩個(gè)職責(zé)進(jìn)行分離男应。
相關(guān)設(shè)計(jì)模式:訪問者模式。
代碼舉例:
這里注意一下娱仔,迭代器在日常使用當(dāng)中是很廣泛的沐飘,但是我們是用的Jdk還有開源的項(xiàng)目當(dāng)中,這些數(shù)據(jù)結(jié)構(gòu)都會(huì)使用現(xiàn)成的迭代器牲迫,幾乎不會(huì)自己去寫一個(gè)耐朴。我們這里來解讀寫好的迭代器代碼。
舉例一個(gè)業(yè)務(wù)場景盹憎,就是慕課網(wǎng)網(wǎng)站的課程筛峭,首先我們有一個(gè)課程的實(shí)體類,然后進(jìn)行遍歷陪每。
//課程實(shí)體類
public class Course {
private String name;
public Course(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//數(shù)據(jù)操作接口
public interface CourseAggregate {
void addCourse(Course course);
void removeCourse(Course course);
//返回迭代器
CourseIterator getCourseIterator();
}
//實(shí)現(xiàn)操作接口
public class CourseAggregateImpl implements CourseAggregate {
private List courseList;
public CourseAggregateImpl() {
this.courseList = new ArrayList();
}
@Override
public void addCourse(Course course) {
courseList.add(course);
}
@Override
public void removeCourse(Course course) {
courseList.remove(course);
}
@Override
public CourseIterator getCourseIterator() {
//返回實(shí)現(xiàn)迭代器接口的類
return new CourseIteratorImpl(courseList);
}
}
//迭代器接口
public interface CourseIterator {
Course nextCourse();
boolean isLastCourse();
}
//實(shí)現(xiàn)迭代器
public class CourseIteratorImpl implements CourseIterator {
private List courseList;
private int position;
private Course course;
public CourseIteratorImpl(List courseList){
this.courseList=courseList;
}
@Override
public Course nextCourse() {
System.out.println("返回課程,位置是: "+position);
course=(Course)courseList.get(position);
position++;
return course;
}
@Override
public boolean isLastCourse(){
if(position< courseList.size()){
return false;
}
return true;
}
}
//測試類
public class Test {
public static void main(String[] args) {
Course course1 = new Course("Java電商一期");
Course course2 = new Course("Java電商二期");
Course course3 = new Course("Java設(shè)計(jì)模式精講");
Course course4 = new Course("Python課程");
Course course5 = new Course("算法課程");
Course course6 = new Course("前端課程");
CourseAggregate courseAggregate = new CourseAggregateImpl();
courseAggregate.addCourse(course1);
courseAggregate.addCourse(course2);
courseAggregate.addCourse(course3);
courseAggregate.addCourse(course4);
courseAggregate.addCourse(course5);
courseAggregate.addCourse(course6);
System.out.println("-----課程列表-----");
printCourses(courseAggregate);
courseAggregate.removeCourse(course4);
courseAggregate.removeCourse(course5);
System.out.println("-----刪除操作之后的課程列表-----");
printCourses(courseAggregate);
}
public static void printCourses(CourseAggregate courseAggregate){
CourseIterator courseIterator= courseAggregate.getCourseIterator();
while(!courseIterator.isLastCourse()){
Course course=courseIterator.nextCourse();
System.out.println(course.getName());
}
}
}
我們分別創(chuàng)建了CourseAggregate和CourseIterator影晓,然后通過實(shí)現(xiàn)CourseAggregate的類的getCourseIterator()
來獲取你自定義的迭代器,最后在測試類中通過printCourses()
來遍歷課程檩禾。這就是迭代器的基本創(chuàng)建了挂签。
3.策略模式
定義:定義了算法家族,分別封裝起來盼产,讓他們之間可以互相替換饵婆,此模式讓算法的變化不會(huì)影響到使用算法的用戶(應(yīng)用層)∠肥郏可以替換大量的if...else...侨核。
適用場景:系統(tǒng)有很多類草穆,而他們的區(qū)別僅僅在于他們的行為不同。一個(gè)系統(tǒng)需要?jiǎng)討B(tài)地在幾種算法中選擇一種芹关。
優(yōu)點(diǎn):提供了對開閉原則的完美支持续挟。可以避免使用多重條件轉(zhuǎn)移語句侥衬。提高算法的保密性和安全性诗祸。
缺點(diǎn):客戶端必須知道所有的策略類,并且自行來決定使用哪一個(gè)策略類轴总。而且會(huì)產(chǎn)生很多策略類直颅。
相關(guān)設(shè)計(jì)模式:工廠模式、狀態(tài)模式怀樟。
代碼舉例:
電商網(wǎng)站在618會(huì)舉行各種促銷活動(dòng)功偿,促銷是課程的行為,促銷行為往堡,但是有多種實(shí)現(xiàn)械荷。策略模式一般不是單獨(dú)使用的,一般是配合工廠虑灰、單例吨瞎、享元模式來使用的,我們這里創(chuàng)建一個(gè)策略工廠模式來配合實(shí)現(xiàn)穆咐。
//策略促銷接口
public interface PromotionStrategy {
//進(jìn)行促銷
void doPromotion();
}
//無促銷實(shí)現(xiàn)
public class EmptyPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion() {
System.out.println("無促銷");
}
}
//返現(xiàn)實(shí)現(xiàn)
public class FanXianPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion() {
System.out.println("返現(xiàn)促銷颤诀,返回的金額存到慕課網(wǎng)用戶的余額中");
}
}
//滿減實(shí)現(xiàn)
public class ManJIanPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion() {
System.out.println("滿減促銷,滿200減20");
}
}
//立減實(shí)現(xiàn)
public class LiJianPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion() {
System.out.println("立減促銷对湃,課程的價(jià)格直接減去配置的價(jià)格");
}
}
//策略工廠
public class PromotionStrategyFactory {
private static Map<String, PromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<>();
//類加載的時(shí)候
static {
PROMOTION_STRATEGY_MAP.put(PromotionKey.LIJIAN, new LiJianPromotionStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.FANXIAN, new FanXianPromotionStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.MANJIAN, new ManJIanPromotionStrategy());
}
private static final PromotionStrategy NON_PROMOTION = new EmptyPromotionStrategy();
private PromotionStrategyFactory() {
}
public static PromotionStrategy getPromotionStrategy(String promotionStrategKey) {
PromotionStrategy promotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionStrategKey);
return promotionStrategy == null ? NON_PROMOTION : promotionStrategy;
}
private interface PromotionKey {
final String LIJIAN = "LIJIAN";
final String FANXIAN = "FANXIAN";
final String MANJIAN = "MANJIAN";
}
}
//策略活動(dòng)
public class PromotionActivity {
private PromotionStrategy promotionStrategy;
public PromotionActivity(PromotionStrategy promotionStrategy) {
this.promotionStrategy = promotionStrategy;
}
public void executePromotionStrategy(){
promotionStrategy.doPromotion();
}
}
//測試類
public class Test {
public static void main(String[] args) {
//假設(shè)key是外部傳進(jìn)來的
String promotionKey1 = "LIJIAN";
PromotionActivity promotionActivity1 = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey1));
promotionActivity1.executePromotionStrategy();
String promotionKey2 = "MANJIAN";
PromotionActivity promotionActivity2 = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey2));
promotionActivity2.executePromotionStrategy();
String promotionKey3 = "";
PromotionActivity promotionActivity3 = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey3));
promotionActivity3.executePromotionStrategy();
}
}
整個(gè)業(yè)務(wù)邏輯的架構(gòu)不變崖叫,只是替換其中的促銷業(yè)務(wù)邏輯,就可以通過key值來決定你要進(jìn)行什么樣子的促銷策略拍柒,然后將key值傳入工廠策略模式里面進(jìn)行調(diào)用心傀,這樣就可以減少大量的if...else...了,唯一的缺點(diǎn)就是應(yīng)用層必須知道有哪些促銷策略拆讯。
4.解釋器模式
定義:給定一個(gè)語言剧包,定義它的文法的一種表示,并定義一個(gè)解釋器往果,這個(gè)解釋器使用該表示來解釋語言中的句子。為了解釋一種語言一铅,而為語言創(chuàng)建的解釋器陕贮。
適用場景:某個(gè)特定類型問題發(fā)生頻率足夠高,例如腳本語言潘飘,或者日志文件肮之,我們需要對這些文件進(jìn)行解析掉缺,就要通過程序(解釋器)來解決該問題。就是其一般使用開源工具包
優(yōu)點(diǎn):語言由很多類表示戈擒,容易改變及擴(kuò)展此“語言”眶明。
缺點(diǎn):當(dāng)語法規(guī)則數(shù)目太多時(shí),增加了系統(tǒng)復(fù)雜度筐高。
相關(guān)設(shè)計(jì)模式:適配器模式
解釋器模式的使用頻率其實(shí)是很低的搜囱。而且現(xiàn)在實(shí)現(xiàn)解釋器模式一般也是使用開源工具包來進(jìn)行實(shí)現(xiàn),這里就不上代碼舉例了柑土。
5.觀察者模式
優(yōu)點(diǎn):定義了對象之間的一對多的依賴蜀肘,讓多個(gè)觀察者對象同時(shí)監(jiān)聽某一個(gè)主題對象,當(dāng)主題對象發(fā)生變化時(shí)稽屏,它的所有依賴者(觀察者)都會(huì)收到通知并更新扮宠。比如,微信的朋友圈點(diǎn)贊之后狐榔,就代表著你是觀察者坛增,而朋友圈這條信息就是被觀察者就是主題對象,那在這個(gè)主題對象被評論的時(shí)候薄腻,微信就會(huì)通知觀察者即點(diǎn)贊的人收捣,這就是觀察者模式的應(yīng)用、
適用場景:關(guān)聯(lián)行為場景被廓,建立一套觸發(fā)機(jī)制塘匣。
優(yōu)點(diǎn):觀察者和被觀察者之間建立了一個(gè)抽象的耦合。觀察者模式支持廣播通信每篷。
缺點(diǎn):觀察者之間有過多的細(xì)節(jié)依賴扭吁,提高時(shí)間消耗及程序復(fù)雜度。還有使用要得到蜓斧,避免循環(huán)調(diào)用仓蛆。
代碼舉例:
//課程類,繼承Observable挎春,是被觀察者
public class Course extends Observable {
private String CourseName;
public Course(String courseName) {
this.CourseName = courseName;
}
public String getCourseName() {
return CourseName;
}
public void produceQuestion(Course course, Question question) {
System.out.println(question.getUserName() + "在" + course.CourseName+"提交了一個(gè)問題");
setChanged();
notifyObservers(question);
}
}
//問題類
public class Question {
private String userName;
private String questionContent;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getQuestionContent() {
return questionContent;
}
public void setQuestionContent(String questionContent) {
this.questionContent = questionContent;
}
}
//老師看疙,繼承Observer,是觀察者
public class Teacher implements Observer {
private String teacherName;
public Teacher(String teacherName) {
this.teacherName = teacherName;
}
@Override
public void update(Observable o, Object arg) {
Course course = (Course) o;
Question question = (Question) arg;
System.out.println(teacherName + "老師的" + course.getCourseName() + "課程接收到一個(gè)" + question.getUserName() + "提交的問題:" + question.getQuestionContent());
}
}
//測試類
public class Test {
public static void main(String[] args) {
Course course = new Course("Java設(shè)計(jì)模式");
Teacher teacher = new Teacher("alpha");
course.addObserver(teacher);
//業(yè)務(wù)邏輯
Question question = new Question();
question.setUserName("juju");
question.setQuestionContent("java函數(shù)如何編寫");
course.produceQuestion(course, question);
}
}
Course類繼承Observable后就成為了被觀察者直奋,Teacher類繼承Observer接口就成為了被觀察者能庆,需要實(shí)現(xiàn)
update()
方法,這個(gè)方法就是當(dāng)被觀察者發(fā)生改變的時(shí)候會(huì)調(diào)用的方法脚线。在測試類中搁胆,通過addObserver()
則就可以添加多個(gè)觀察者,然后調(diào)用Course類的produceQuestion()
方法,在produceQuestion()
方法里面渠旁,會(huì)調(diào)用Observable的setChanged()
方法攀例,表示被觀察者發(fā)生改變,然后通過notifyObservers()
可以傳遞需要處理的對象或者業(yè)務(wù)邏輯顾腊,這個(gè)對象從update()
中的第二個(gè)參數(shù)可以獲取粤铭,第一個(gè)參數(shù)為被觀察者對象。6.備忘錄模式
定義: 保存一個(gè)對象的某個(gè)狀態(tài)杂靶,以便在適當(dāng)?shù)臓顟B(tài)恢復(fù)對象梆惯,一句話,后悔藥伪煤。
適用場景:保存及恢復(fù)數(shù)據(jù)相關(guān)的業(yè)務(wù)場景加袋。還有后悔的時(shí)候,想恢復(fù)之間的狀態(tài)抱既。
優(yōu)點(diǎn):為用戶提供一種可恢復(fù)的機(jī)制和存檔信息的封裝职烧。
缺點(diǎn):資源占用。
相關(guān)設(shè)計(jì)模式:狀態(tài)模式防泵。
我們這里引入一個(gè)業(yè)務(wù)場景蚀之,就是在網(wǎng)站上面發(fā)布一篇手記,然后其中有暫存的功能捷泞,而這個(gè)功能就可以通過備忘錄設(shè)計(jì)模式來進(jìn)行實(shí)現(xiàn)足删。
代碼舉例:
//手記類
public class Article {
private String title;
private String content;
private String imgs;
public Article(String title, String content, String imgs) {
this.title = title;
this.content = content;
this.imgs = imgs;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getImgs() {
return imgs;
}
public void setImgs(String imgs) {
this.imgs = imgs;
}
//保存快照
public ArticleMemento saveToMemento() {
ArticleMemento articleMemento = new ArticleMemento(this.title,this.content,this.imgs);
return articleMemento;
}
//恢復(fù)快照
public void undoFromMemento(ArticleMemento articleMemento) {
this.title = articleMemento.getTitle();
this.content = articleMemento.getContent();
this.imgs = articleMemento.getImgs();
}
@Override
public String toString() {
return "Article{" +
"title='" + title + '\'' +
", content='" + content + '\'' +
", imgs='" + imgs + '\'' +
'}';
}
}
//快照類
public class ArticleMemento {
private String title;
private String content;
private String imgs;
public ArticleMemento(String title, String content, String imgs) {
this.title = title;
this.content = content;
this.imgs = imgs;
}
public String getTitle() {
return title;
}
public String getContent() {
return content;
}
public String getImgs() {
return imgs;
}
@Override
public String toString() {
return "ArticleMemento{" +
"title='" + title + '\'' +
", content='" + content + '\'' +
", imgs='" + imgs + '\'' +
'}';
}
}
首先創(chuàng)建一個(gè)Article類,也就是手記類锁右,里面有兩個(gè)重要的方法失受,saveToMemento()
和undoFromMemento()
方法。saveToMemento()
主要就是會(huì)創(chuàng)建一個(gè)ArticleMemento實(shí)例咏瑟,也就是快照實(shí)例拂到,然后把需要保存的數(shù)據(jù)放入到ArticleMemento實(shí)例中保存下來,undoFromMemento()
主要就是恢復(fù)那些已經(jīng)保存的快照實(shí)例里面的值码泞,這里注意一下兄旬,ArticleMemento也就是快照類是不需要set()
方法的,它只能通過構(gòu)造函數(shù)來傳遞數(shù)值余寥。然后领铐,我們在創(chuàng)建一個(gè)管理這些快照的管理類。
public class ArticleMementoManager {
private final Stack<ArticleMemento> ARTICLE_MEMENTO_STACK = new Stack<>();
public ArticleMemento getMemento()
{
ArticleMemento articleMemento= ARTICLE_MEMENTO_STACK.pop();
return articleMemento;
}
public void addMemento(ArticleMemento articleMemento)
{
ARTICLE_MEMENTO_STACK.push(articleMemento);
}
}
管理方式通過Stack來進(jìn)行管理宋舷,getMemento()
就是出棧绪撵,獲得最新保存的快照實(shí)例,addMemento()
就是入棧操作了祝蝠。
我們從UML中可以看出莲兢,ArticleMemento類汹来,也就是快照類是只能被Article類來進(jìn)行創(chuàng)造的,ArticleMemento類和ArticleMementoManager 類是一種聚合關(guān)系改艇,因?yàn)锳rticleMementoManager類可以有多個(gè)ArticleMemento。
//測試類
public class Test {
public static void main(String[] args) {
ArticleMementoManager articleMementoManager = new ArticleMementoManager();
Article article= new Article("如影隨行的設(shè)計(jì)模式A","手記內(nèi)容A","手記圖片A");
ArticleMemento articleMemento = article.saveToMemento();
articleMementoManager.addMemento(articleMemento);
System.out.println("標(biāo)題:"+article.getTitle()+" 內(nèi)容:"+article.getContent()+" 圖片:"+article.getImgs()+" 暫存成功");
System.out.println("手記完整信息:"+article);
System.out.println("修改手記start");
article.setTitle("如影隨行的設(shè)計(jì)模式B");
article.setContent("手記內(nèi)容B");
article.setImgs("手記圖片B");
System.out.println("修改手記end");
System.out.println("手記完整信息:"+article);
articleMemento = article.saveToMemento();
articleMementoManager.addMemento(articleMemento);
article.setTitle("如影隨行的設(shè)計(jì)模式C");
article.setContent("手記內(nèi)容C");
article.setImgs("手記圖片C");
System.out.println("暫存回退start");
System.out.println("回退出棧1次");
articleMemento = articleMementoManager.getMemento();
article.undoFromMemento(articleMemento);
System.out.println("回退出棧2次");
articleMemento = articleMementoManager.getMemento();
article.undoFromMemento(articleMemento);
System.out.println("暫存回退end");
System.out.println("手記完整信息:"+article);
}
}
7.命令模式
定義:將“請求”封裝為對象坟岔,以便使用不同的請求谒兄。命令模式解決了應(yīng)用程序中對象的職責(zé)以及它們之間的通信方式。命令模式可以使發(fā)送者和接收者完全解耦社付,發(fā)送命令的對象只知道如何發(fā)送請求承疲,不需要只要如何完成請求。
適用場景:請求調(diào)用者和請求接收者需要解耦鸥咖,使得調(diào)用者和接收者不直接交互燕鸽。需要抽象出等待執(zhí)行的行為。
優(yōu)點(diǎn):降低耦合啼辣。容易擴(kuò)展新命令或者一組命令啊研。
缺點(diǎn):命令的無限擴(kuò)展會(huì)增加類的數(shù)量,提高系統(tǒng)實(shí)現(xiàn)復(fù)雜度鸥拧。
相關(guān)設(shè)計(jì)模式:備忘錄模式党远,我們可以通過備忘錄保存命令的歷史記錄。
假設(shè)我們開發(fā)一個(gè)功能富弦,關(guān)閉一個(gè)課程的視頻沟娱,或者局部關(guān)閉某一小節(jié)或者打開。對于這些課程來說腕柜,第一章節(jié)是免費(fèi)開放的济似,后續(xù)章節(jié)需要購買之后才能學(xué)習(xí)觀看,假如說下了一個(gè)命令盏缤,免費(fèi)開放了第二章砰蠢,又過了一陣,免費(fèi)開放了第三章蛾找。然后又過了一陣把第二章節(jié)又關(guān)閉了娩脾。
代碼舉例:
//命令接口
public interface Command {
void execute();
}
//課程視頻類
public class CourseVideo {
private String name;
public CourseVideo(String name) {
this.name = name;
}
public void open() {
System.out.println(this.name + "課程視頻開發(fā)");
}
public void close() {
System.out.println(this.name + "課程視頻關(guān)閉");
}
}
//關(guān)閉視頻命令
public class CloseCourseVideoCommand implements Command {
private CourseVideo courseVideo;
public CloseCourseVideoCommand(CourseVideo courseVideo) {
this.courseVideo = courseVideo;
}
@Override
public void execute() {
courseVideo.close();
}
}
//打開視頻命令
public class OpenCourseVideoCommand implements Command {
private CourseVideo courseVideo;
public OpenCourseVideoCommand(CourseVideo courseVideo) {
this.courseVideo = courseVideo;
}
@Override
public void execute() {
courseVideo.open();
}
}
首先創(chuàng)建一個(gè)命令接口和一個(gè)CourseVideo類,CourseVideo類里面有要執(zhí)行的方法打毛,我們的目標(biāo)就是包裝這些方法(命令)柿赊,接著創(chuàng)建OpenCourseVideoCommand類和CloseCourseVideoCommand類,這兩個(gè)類繼承Command接口幻枉,構(gòu)造函數(shù)傳入CourseVideo實(shí)例碰声,接著在execute()
里實(shí)現(xiàn)你要封裝的命令,接下來要執(zhí)行這些命令或者一組命令肯定要有一個(gè)執(zhí)行者熬甫,接著創(chuàng)建一個(gè)Staff類:
public class Staff {
private List<Command> commandList = new ArrayList<>();
public void addCommand(Command command) {
commandList.add(command);
}
public void executeCommands() {
for (Command command : commandList) {
command.execute();
}
commandList.clear();
}
}
Staff類里面有一個(gè)List胰挑,里面放入的就是一組命令,通過addCommand()
方法可以放入你想要組合的命令,最后通過executeCommands()
來執(zhí)行瞻颂,執(zhí)行完畢后會(huì)通過clear()
來清除這個(gè)集合命令豺谈。
從UML圖可以看出,首先CloseCourseVideoCommand和OpenCourseVideoCommand是命令它們實(shí)現(xiàn)Command接口贡这,Staff里面可以有多個(gè)Command或者一個(gè)茬末。也就是說如果我們擴(kuò)展Command,Command和Staff已經(jīng)是一對多的關(guān)系盖矫,所以Staff里面的代碼是不用動(dòng)的丽惭。
public class Test {
public static void main(String[] args) {
CourseVideo courseVideo = new CourseVideo("Java設(shè)計(jì)模式-命令模式");
OpenCourseVideoCommand openCourseVideoCommand = new OpenCourseVideoCommand(courseVideo);
CloseCourseVideoCommand closeCourseVideoCommand = new CloseCourseVideoCommand(courseVideo);
Staff staff = new Staff();
staff.addCommand(openCourseVideoCommand);
staff.addCommand(closeCourseVideoCommand);
staff.executeCommands();
}
}
8.中介者模式
定義:定義一個(gè)封裝一組對象如何交互的對象。通過使對象明確的相互引用來促進(jìn)松散耦合辈双,并允許獨(dú)立地改變它們的交互责掏。
適用場景:在系統(tǒng)中對象之間存在復(fù)雜的引用關(guān)系,產(chǎn)生的相互依賴關(guān)系結(jié)構(gòu)混亂切難以理解湃望。交互的公共行為换衬,如果需要改變行為則可以增加新的中介者類。
優(yōu)點(diǎn):將一對多轉(zhuǎn)化成了一對一喜爷、降低程序復(fù)雜度冗疮,以及類之間的解耦。
缺點(diǎn):中介者過多檩帐,導(dǎo)致系統(tǒng)復(fù)雜术幔。
相關(guān)設(shè)計(jì)模式:觀察者模式(有時(shí)候會(huì)結(jié)合使用)。
我們引入一個(gè)業(yè)務(wù)場景湃密,我們所有的課程都有一個(gè)學(xué)習(xí)的群诅挑,現(xiàn)在使用的是QQ群,對于學(xué)習(xí)群來說泛源,小伙伴在里面交流的介質(zhì)拔妥。
代碼舉例:
//中介者類
public class StudyGroup {
public static void showMessage(User user, String message){
System.out.println(new Date().toString() + " [" + user.getName() + "] : " + message);
}
}
//用戶類
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String name) {
this.name = name;
}
public void sendMessage(String message) {
StudyGroup.showMessage(this, message);
}
}
//測試類
public class Test {
public static void main(String[] args) {
User geely = new User("Geely");
User tom= new User("Tom");
geely.sendMessage(" Hey! Tom! Let's learn Design Pattern");
tom.sendMessage("OK! Geely");
}
}
StudyGroup類就是一個(gè)中介者類,里面有一個(gè)showMessage()
方法达箍,里面?zhèn)魅雞ser和message没龙,是一個(gè)靜態(tài)方法,接著看User類缎玫,里面有一個(gè)sendMessage()
方法硬纤,對于A用戶來說,如果在調(diào)用sendMessage()
的時(shí)候赃磨,只和這個(gè)中介者模式打交道就可以了筝家,A用戶發(fā)送了一個(gè)消息,然后交由StudyGroup類也就是中介者來進(jìn)行展示消息邻辉。這就是中介者模式的核心溪王。
9.責(zé)任鏈模式
定義:為請求創(chuàng)建一個(gè)接收此次請求對象的鏈腮鞍。
適用場景:一個(gè)請求的處理需要多個(gè)對象當(dāng)中的一個(gè)或幾個(gè)協(xié)作處理。
優(yōu)點(diǎn):請求的發(fā)送者和接收者(請求的處理)解耦莹菱,而且責(zé)任鏈也可以動(dòng)態(tài)組合移国。
缺點(diǎn):如果責(zé)任鏈太長或者處理時(shí)間過長,影響性能道伟,責(zé)任鏈有可能過多桥狡。
相關(guān)設(shè)計(jì)模式:狀態(tài)模式。
引入一個(gè)業(yè)務(wù)場景皱卓,也就是說線上的課程在發(fā)布的時(shí)候,經(jīng)過兩個(gè)人批準(zhǔn)部逮,一個(gè)是手記的檢查人娜汁,另一個(gè)是視頻的檢查人。
代碼舉例:
//課程類
public class Course {
private String name;
private String article;
private String Video;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getArticle() {
return article;
}
public void setArticle(String article) {
this.article = article;
}
public String getVideo() {
return Video;
}
public void setVideo(String video) {
Video = video;
}
@Override
public String toString() {
return "Course{" +
"name='" + name + '\'' +
", article='" + article + '\'' +
", Video='" + Video + '\'' +
'}';
}
}
//責(zé)任鏈接口
public abstract class Approver {
protected Approver approver;
public void setNextApprover(Approver approver){
this.approver = approver;
}
public abstract void deploy(Course course);
}
//手記檢查類
public class ArticleApprover extends Approver {
@Override
public void deploy(Course course) {
if (course.getArticle() != null) {
System.out.println(course.getName() + "含有手記批準(zhǔn)");
if (approver != null){
approver.deploy(course);
}
}else {
System.out.println(course.getName()+"不包含手記兄朋,不批準(zhǔn)掐禁,流程結(jié)束");
return;
}
}
}
//視頻檢查類
public class VideoApprover extends Approver{
@Override
public void deploy(Course course) {
if (course.getVideo() != null) {
System.out.println(course.getName() + "含有視頻批準(zhǔn)");
if (approver != null){
approver.deploy(course);
}
}else {
System.out.println(course.getName()+"不包含視頻,不批準(zhǔn)颅和,流程結(jié)束");
return;
}
}
}
首先創(chuàng)建一個(gè)責(zé)任鏈接口Approver 傅事,里面deploy()
主要就是每個(gè)攔截器具體處理的業(yè)務(wù),還有一個(gè)setNextApprover()
方法峡扩,這個(gè)方法也是責(zé)任鏈模式的核心方法蹭越,它決定這當(dāng)前攔截器處理完畢后要執(zhí)行的下一個(gè)攔截器的方法,類似數(shù)據(jù)結(jié)構(gòu)的鏈表教届,然后創(chuàng)建了ArticleApprover和VideoApprover類并實(shí)現(xiàn)了Approver接口响鹃,在deploy()
方法里面編寫具體實(shí)現(xiàn)的代碼,在執(zhí)行業(yè)務(wù)邏輯后案训,開始判斷是否還有下一個(gè)攔截器买置,如果有,就執(zhí)行下一個(gè)攔截器的業(yè)務(wù)邏輯强霎,如果沒有忿项,則攔截鏈執(zhí)行完畢。
public class Test {
public static void main(String[] args) {
Approver artApprover = new ArticleApprover();
Approver videoApprover = new VideoApprover();
Course course = new Course();
course.setName("java");
course.setArticle("shouji"); //把這行代碼進(jìn)行注釋城舞,artApprover攔截器就會(huì)截止轩触,不會(huì)繼續(xù)往下面執(zhí)行
course.setVideo("video");
artApprover.setNextApprover(videoApprover);
artApprover.deploy(course);
}
}
在測試類中,artApprover通過setNextApprover()
來設(shè)置下一個(gè)要處理的攔截器為videoApprover椿争,當(dāng)artApprover的攔截器處理完畢后就會(huì)執(zhí)行videoApprover攔截器的業(yè)務(wù)邏輯怕膛,但是當(dāng)artApprover的攔截器處理結(jié)果是有問題的,則不會(huì)處理下一個(gè)攔截器秦踪,會(huì)直接終止整個(gè)攔截器鏈褐捻。
10.訪問者模式
定義:封裝作用于某數(shù)據(jù)結(jié)構(gòu)(如List/Set/Map等)中的各元素的操作掸茅。可以在不改變各元素類的前提下柠逞,定義作用于這些元素的操作昧狮。訪問者模式平時(shí)用的其實(shí)非常少。
適用場景:一個(gè)數(shù)據(jù)結(jié)構(gòu)(如List/Set/Map等)包含很多類型對象板壮,數(shù)據(jù)結(jié)構(gòu)與數(shù)據(jù)操作分離的需求逗鸣。
優(yōu)點(diǎn):增加新的操作很容易,即增加新的訪問者绰精。
缺點(diǎn):增加新的數(shù)據(jù)結(jié)構(gòu)困難撒璧,具體元素變更比較麻煩。
相關(guān)設(shè)計(jì)模式:迭代器模式笨使。
引入一個(gè)業(yè)務(wù)場景卿樱,線上有免費(fèi)課程FreeCourse和實(shí)戰(zhàn)課程CodingCourse,然后對這些課程進(jìn)行遍歷硫椰。
代碼舉例:
//課程抽象類
public abstract class Course {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void accept(IVisitor visitor);
}
//實(shí)戰(zhàn)課程類
public class CodingCourse extends Course {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
//免費(fèi)課程類
public class FreeCourse extends Course {
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
//訪問者接口
public interface IVisitor {
void visit(FreeCourse freeCourse);
void visit(CodingCourse codingCourse);
}
//訪問者實(shí)現(xiàn)類
public class Visitor implements IVisitor {
//訪問免費(fèi)課程繁调,打印所有免費(fèi)課程名稱
@Override
public void visit(FreeCourse freeCourse) {
System.out.println("免費(fèi)課程:"+freeCourse.getName());
}
//訪問實(shí)戰(zhàn)課程,打印所有實(shí)戰(zhàn)課程名稱及價(jià)格
@Override
public void visit(CodingCourse codingCourse) {
System.out.println("實(shí)戰(zhàn)課程:"+codingCourse.getName()+" 價(jià)格:"+codingCourse.getPrice()+"元");
}
}
首先定義一個(gè)Course抽象類靶草,具體的業(yè)務(wù)邏輯實(shí)現(xiàn)zeyou則由CodingCourse和FreeCourse來進(jìn)行是實(shí)現(xiàn)蹄胰,具體業(yè)務(wù)邏輯方法是放在accept()
方法中,接下來定義一個(gè)訪問者IVisitor接口奕翔,并創(chuàng)建一個(gè)Visitor類來實(shí)現(xiàn)和這個(gè)訪問者接口裕寨,這里需要注意一下,如果需要新的訪問接口糠悯,就可以直接在IVisitor接口中進(jìn)行定義帮坚,具體的展示都是在Visitor里實(shí)現(xiàn)的方法來具體實(shí)現(xiàn)。
public class Test {
public static void main(String[] args) {
List<Course> courseList = new ArrayList<>();
FreeCourse freeCourse = new FreeCourse();
freeCourse.setName("SpringMVC數(shù)據(jù)綁定");
CodingCourse codingCourse = new CodingCourse();
codingCourse.setName("Java設(shè)計(jì)模式精講 -- By Geely");
codingCourse.setPrice(299);
courseList.add(freeCourse);
courseList.add(codingCourse);
for(Course course : courseList){
course.accept(new Visitor());
}
}
}
在測試類中互艾,最后的循環(huán)那里需要一個(gè)訪問者试和,具體的實(shí)現(xiàn)都是在這個(gè)類里面了,應(yīng)用層就無需在糾結(jié)具體的業(yè)務(wù)邏輯的實(shí)現(xiàn)纫普。
11.狀態(tài)模式
定義:允許一個(gè)對象在其內(nèi)部狀態(tài)改變時(shí)阅悍,改變它的行為。
適用場景:一個(gè)對象存在多個(gè)狀態(tài)(不同狀態(tài)下行為不同)昨稼,且狀態(tài)可相互轉(zhuǎn)換节视。
優(yōu)點(diǎn):將不同的狀態(tài)隔離,把各種狀態(tài)的轉(zhuǎn)換邏輯假栓,分不到State的子類中寻行,減少相互依賴。增加新的狀態(tài)非常簡單匾荆。
缺點(diǎn):狀態(tài)多的業(yè)務(wù)場景導(dǎo)致數(shù)目增加拌蜘,系統(tǒng)變復(fù)雜杆烁。
相關(guān)設(shè)計(jì)模式:享元模式
引入一個(gè)業(yè)務(wù)場景,就是網(wǎng)站的課程視頻简卧,有暫停兔魂、播放、快進(jìn)和停止举娩,在停止的時(shí)候是無法快進(jìn)的析校。所以如果是停止的狀態(tài),在轉(zhuǎn)換成快進(jìn)的時(shí)候要做一個(gè)校驗(yàn)铜涉。
代碼舉例:
//視頻上下文類
public class CourseVideoContext {
private CourseVideoState courseVideoState;
public final static PlayState PLAY_STATE = new PlayState();
public final static StopState STOP_STATE = new StopState();
public final static PauseState PAUSE_STATE = new PauseState();
public final static SpeedState SPEED_STATE = new SpeedState();
public CourseVideoState getCourseVideoState() {
return courseVideoState;
}
public void setCourseVideoState(CourseVideoState courseVideoState) {
this.courseVideoState = courseVideoState;
this.courseVideoState.setCourseVideoContext(this);
}
public void play(){
this.courseVideoState.play();
}
public void speed(){
this.courseVideoState.speed();
}
public void stop(){
this.courseVideoState.stop();
}
public void pause(){
this.courseVideoState.pause();
}
}
//視頻狀態(tài)類
public abstract class CourseVideoState {
protected CourseVideoContext courseVideoContext;
public void setCourseVideoContext(CourseVideoContext courseVideoContext) {
this.courseVideoContext = courseVideoContext;
}
public abstract void play();
public abstract void speed();
public abstract void pause();
public abstract void stop();
}
首先創(chuàng)建了一個(gè)CourseVideoContext類智玻,這是這個(gè)設(shè)計(jì)模式的核心類,里面持有了四個(gè)狀態(tài)的實(shí)例和一個(gè)CourseVideoState類芙代,getCourseVideoState()
方法為獲取視頻的當(dāng)前狀態(tài)尚困,setCourseVideoState()
為設(shè)置視頻的狀態(tài)。
//暫停類
public class PauseState extends CourseVideoState {
@Override
public void play() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
}
@Override
public void speed() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.SPEED_STATE);
}
@Override
public void pause() {
System.out.println("暫停播放課程視頻狀態(tài)");
}
@Override
public void stop() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
}
}
//播放類
public class PlayState extends CourseVideoState {
@Override
public void play() {
System.out.println("正常播放課程視頻狀態(tài)");
}
@Override
public void speed() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.SPEED_STATE);
}
@Override
public void pause() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PAUSE_STATE);
}
@Override
public void stop() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
}
}
//停止類
public class StopState extends CourseVideoState {
@Override
public void play() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
}
@Override
public void speed() {
System.out.println("ERROR 停止?fàn)顟B(tài)不能快進(jìn)!!");
}
@Override
public void pause() {
System.out.println("ERROR 停止?fàn)顟B(tài)不能暫停!!");
}
@Override
public void stop() {
System.out.println("停止播放課程視頻狀態(tài)");
}
}
//快進(jìn)類
public class SpeedState extends CourseVideoState {
@Override
public void play() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
}
@Override
public void speed() {
System.out.println("快進(jìn)播放課程視頻狀態(tài)");
}
@Override
public void pause() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PAUSE_STATE);
}
@Override
public void stop() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
}
}
//測試類
public class Test {
public static void main(String[] args) {
CourseVideoContext courseVideoContext = new CourseVideoContext();
courseVideoContext.setCourseVideoState(new PlayState());
System.out.println("當(dāng)前狀態(tài):"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.pause();
System.out.println("當(dāng)前狀態(tài):"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.speed();
System.out.println("當(dāng)前狀態(tài):"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.stop();
System.out.println("當(dāng)前狀態(tài):"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.speed();
}
}
在測試類中链蕊,創(chuàng)建了一個(gè)CourseVideoContext實(shí)例,然后通過setCourseVideoState()
方法設(shè)置視頻狀態(tài)為PlayState谬泌,然后可以通過getCourseVideoState()
來獲取當(dāng)前視頻的狀態(tài)滔韵。也可以直接調(diào)用courseVideoContext類里的方法去改變狀態(tài)。