没有性能瓶颈的无限极菜单树应该这样设计
本文節(jié)選自《設(shè)計(jì)模式就該這樣學(xué)》
1 使用透明組合模式實(shí)現(xiàn)課程目錄結(jié)構(gòu)
以一門網(wǎng)絡(luò)課程為例,我們?cè)O(shè)計(jì)一個(gè)課程的關(guān)系結(jié)構(gòu)。比如,我們有Java入門課程、人工智能課程、Java設(shè)計(jì)模式、源碼分析、軟技能等,而Java設(shè)計(jì)模式、源碼分析、軟技能又屬于Java架構(gòu)師系列課程包,每個(gè)課程的定價(jià)都不一樣。但是,這些課程不論怎么組合,都有一些共性,而且是整體和部分的關(guān)系,可以用組合模式來設(shè)計(jì)。首先創(chuàng)建一個(gè)頂層的抽象組件CourseComponent類。
/*** Created by Tom.*/ public abstract class CourseComponent {public void addChild(CourseComponent catalogComponent){throw new UnsupportedOperationException("不支持添加操作");}public void removeChild(CourseComponent catalogComponent){throw new UnsupportedOperationException("不支持刪除操作");}public String getName(CourseComponent catalogComponent){throw new UnsupportedOperationException("不支持獲取名稱操作");}public double getPrice(CourseComponent catalogComponent){throw new UnsupportedOperationException("不支持獲取價(jià)格操作");}public void print(){throw new UnsupportedOperationException("不支持打印操作");}}把所有可能用到的方法都定義到這個(gè)頂層的抽象組件中,但是不寫任何邏輯處理的代碼,而是直接拋異常。這里,有些小伙伴會(huì)有疑惑,為什么不用抽象方法?因?yàn)橛昧顺橄蠓椒?#xff0c;其子類就必須實(shí)現(xiàn),這樣便體現(xiàn)不出各子類的細(xì)微差異。所以子類繼承此抽象類后,只需要重寫有差異的方法覆蓋父類的方法即可。然后分別創(chuàng)建課程Course類和課程包CoursePackage類。創(chuàng)建Course類的代碼如下。
/*** Created by Tom.*/ public class Course extends CourseComponent {private String name;private double price;public Course(String name, double price) {this.name = name;this.price = price;}@Overridepublic String getName(CourseComponent catalogComponent) {return this.name;}@Overridepublic double getPrice(CourseComponent catalogComponent) {return this.price;}@Overridepublic void print() {System.out.println(name + " (¥" + price + "元)");}}創(chuàng)建CoursePackage類的代碼如下。
/*** Created by Tom.*/ public class CoursePackage extends CourseComponent {private List<CourseComponent> items = new ArrayList<CourseComponent>();private String name;private Integer level;public CoursePackage(String name, Integer level) {this.name = name;this.level = level;}@Overridepublic void addChild(CourseComponent catalogComponent) {items.add(catalogComponent);}@Overridepublic String getName(CourseComponent catalogComponent) {return this.name;}@Overridepublic void removeChild(CourseComponent catalogComponent) {items.remove(catalogComponent);}@Overridepublic void print() {System.out.println(this.name);for(CourseComponent catalogComponent : items){//控制顯示格式if(this.level != null){for(int i = 0; i < this.level; i ++){//打印空格控制格式System.out.print(" ");}for(int i = 0; i < this.level; i ++){//每一行開始打印一個(gè)+號(hào)if(i == 0){ System.out.print("+"); }System.out.print("-");}}//打印標(biāo)題catalogComponent.print();}}}最后編寫客戶端測(cè)試代碼。
public static void main(String[] args) {System.out.println("============透明組合模式===========");CourseComponent javaBase = new Course("Java入門課程",8280);CourseComponent ai = new Course("人工智能",5000);CourseComponent packageCourse = new CoursePackage("Java架構(gòu)師課程",2);CourseComponent design = new Course("Java設(shè)計(jì)模式",1500);CourseComponent source = new Course("源碼分析",2000);CourseComponent softSkill = new Course("軟技能",3000);packageCourse.addChild(design);packageCourse.addChild(source);packageCourse.addChild(softSkill);CourseComponent catalog = new CoursePackage("課程主目錄",1);catalog.addChild(javaBase);catalog.addChild(ai);catalog.addChild(packageCourse);catalog.print();}運(yùn)行結(jié)果如下圖所示。
透明組合模式把所有公共方法都定義在 Component 中,這樣客戶端就不需要區(qū)分操作對(duì)象是葉子節(jié)點(diǎn)還是樹枝節(jié)點(diǎn);但是,葉子節(jié)點(diǎn)會(huì)繼承一些它不需要(管理子類操作的方法)的方法,這與設(shè)計(jì)模式的接口隔離原則相違背。
2 使用安全組合模式實(shí)現(xiàn)無限級(jí)文件系統(tǒng)
再舉一個(gè)程序員更熟悉的例子。對(duì)于程序員來說,電腦是每天都要接觸的。電腦的文件系統(tǒng)其實(shí)就是一個(gè)典型的樹形結(jié)構(gòu),目錄包含文件夾和文件,文件夾里面又可以包含文件夾和文件。下面用代碼來實(shí)現(xiàn)一個(gè)目錄系統(tǒng)。文件系統(tǒng)有兩個(gè)大的層次:文件夾和文件。其中,文件夾能容納其他層次,為樹枝節(jié)點(diǎn);文件是最小單位,為葉子節(jié)點(diǎn)。由于目錄系統(tǒng)層次較少,且樹枝節(jié)點(diǎn)(文件夾)結(jié)構(gòu)相對(duì)穩(wěn)定,而文件其實(shí)可以有很多類型,所以我們選擇使用安全組合模式來實(shí)現(xiàn)目錄系統(tǒng),可以避免為葉子節(jié)點(diǎn)類型(文件)引入冗余方法。首先創(chuàng)建頂層的抽象組件Directory類。
public abstract class Directory {protected String name;public Directory(String name) {this.name = name;}public abstract void show();}然后分別創(chuàng)建File類和Folder類。創(chuàng)建File類的代碼如下。
public class File extends Directory {public File(String name) {super(name);}@Overridepublic void show() {System.out.println(this.name);}}創(chuàng)建Folder類的代碼如下。
import java.util.ArrayList; import java.util.List;public class Folder extends Directory {private List<Directory> dirs;private Integer level;public Folder(String name,Integer level) {super(name);this.level = level;this.dirs = new ArrayList<Directory>();}@Overridepublic void show() {System.out.println(this.name);for (Directory dir : this.dirs) {//控制顯示格式if(this.level != null){for(int i = 0; i < this.level; i ++){//打印空格控制格式System.out.print(" ");}for(int i = 0; i < this.level; i ++){//每一行開始打印一個(gè)+號(hào)if(i == 0){ System.out.print("+"); }System.out.print("-");}}//打印名稱dir.show();}}public boolean add(Directory dir) {return this.dirs.add(dir);}public boolean remove(Directory dir) {return this.dirs.remove(dir);}public Directory get(int index) {return this.dirs.get(index);}public void list(){for (Directory dir : this.dirs) {System.out.println(dir.name);}}}注意,Folder類不僅覆蓋了頂層的show()方法,還增加了list()方法。最后編寫客戶端測(cè)試代碼。
public static void main(String[] args) {System.out.println("============安全組合模式===========");File qq = new File("QQ.exe");File wx = new File("微信.exe");Folder office = new Folder("辦公軟件",2);File word = new File("Word.exe");File ppt = new File("PowerPoint.exe");File excel = new File("Excel.exe");office.add(word);office.add(ppt);office.add(excel);Folder wps = new Folder("金山軟件",3);wps.add(new File("WPS.exe"));office.add(wps);Folder root = new Folder("根目錄",1);root.add(qq);root.add(wx);root.add(office);System.out.println("----------show()方法效果-----------");root.show();System.out.println("----------list()方法效果-----------");root.list();}運(yùn)行結(jié)果如下圖所示。
安全組合模式的好處是接口定義職責(zé)清晰,符合設(shè)計(jì)模式的單一職責(zé)原則和接口隔離原則;缺點(diǎn)是客戶需要區(qū)分樹枝節(jié)點(diǎn)和葉子節(jié)點(diǎn),這樣才能正確處理各個(gè)層次的操作,客戶端無法依賴抽象接口(Component),違背了設(shè)計(jì)模式的依賴倒置原則。
本文為“Tom彈架構(gòu)”原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處。技術(shù)在于分享,我分享我快樂!
如果本文對(duì)您有幫助,歡迎關(guān)注和點(diǎn)贊;如果您有任何建議也可留言評(píng)論或私信,您的支持是我堅(jiān)持創(chuàng)作的動(dòng)力。關(guān)注微信公眾號(hào)『 Tom彈架構(gòu) 』可獲取更多技術(shù)干貨!
本文分享自微信公眾號(hào) - Tom彈架構(gòu)(gh_e3be84a8ccb2)。
如有侵權(quán),請(qǐng)聯(lián)系 support@oschina.cn 刪除。
本文參與“ ?OSC源創(chuàng)計(jì)劃? ”,歡迎正在閱讀的你也加入,一起分享。
總結(jié)
以上是生活随笔為你收集整理的没有性能瓶颈的无限极菜单树应该这样设计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python打印等腰梯形_Python科
- 下一篇: 韩顺平Java:qq项目离线发送接收消息