大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動(dòng)力節(jié)點(diǎn)LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 設(shè)計(jì)模式中的組合模式詳解

設(shè)計(jì)模式中的組合模式詳解

更新時(shí)間:2022-10-11 10:10:49 來源:動(dòng)力節(jié)點(diǎn) 瀏覽992次

設(shè)計(jì)模式中的組合模式是什么?小編來告訴大家。將一組對象組織(Compose)成樹形結(jié)構(gòu),以表示一種“部分 - 整體”的層次結(jié)構(gòu)。組合讓客戶端可以統(tǒng)一單個(gè)對象和組合對象的處理邏輯。接下來,對于組合模式,舉個(gè)例子來給你解釋一下。

假設(shè)我們有這樣一個(gè)需求:設(shè)計(jì)一個(gè)類來表示文件系統(tǒng)中的目錄,能方便地實(shí)現(xiàn)下面這些功能:

動(dòng)態(tài)地添加、刪除某個(gè)目錄下的子目錄或文件;

統(tǒng)計(jì)指定目錄下的文件個(gè)數(shù);

統(tǒng)計(jì)指定目錄下的文件總大小。

這里給出了這個(gè)類的骨架代碼,如下所示。其中的核心邏輯并未實(shí)現(xiàn),你可以試著自己去補(bǔ)充完整,再來看我的講解。在下面的代碼實(shí)現(xiàn)中,我們把文件和目錄統(tǒng)一用 FileSystemNode 類來表示,并且通過File類屬性來區(qū)分。

public class FileSystemNode {
  private String path;
  private boolean isFile;
  private List<FileSystemNode> subNodes = new ArrayList<>();
  public FileSystemNode(String path, boolean isFile) {
    this.path = path;
    this.isFile = isFile;
  }
  public int countNumOfFiles() {
    // TODO:...
  }
  public long countSizeOfFiles() {
    // TODO:...
  }
  public String getPath() {
    return path;
  }
  public void addSubNode(FileSystemNode fileOrDir) {
    subNodes.add(fileOrDir);
  }
  public void removeSubNode(FileSystemNode fileOrDir) {
    int size = subNodes.size();
    int i = 0;
    for (; i < size; ++i) {
      if (subNodes.get(i).getPath().equalsIgnoreCase(fileOrDir.getPath())) {
        break;
      }
    }
    if (i < size) {
      subNodes.remove(i);
    }
  }
}

想要補(bǔ)全其中的 countNumOfFiles() 和 countSizeOfFiles() 這兩個(gè)函數(shù),并不是件難事,實(shí)際上這就是樹上的遞歸遍歷算法。對于文件,我們直接返回文件的個(gè)數(shù)(返回 1)或大小。對于目錄,我們遍歷目錄中每個(gè)子目錄或者文件,遞歸計(jì)算它們的個(gè)數(shù)或大小,然后求和,就是這個(gè)目錄下的文件個(gè)數(shù)和文件大小。

public int countNumOfFiles() {
    if (isFile) {
        return 1;
    }
    int numOfFiles = 0;
    for (FileSystemNode fileOrDir : subNodes) {
        numOfFiles += fileOrDir.countNumOfFiles();
    }
    return numOfFiles;
}
public long countSizeOfFiles() {
    if (isFile) {
        File file = new File(path);
        if (!file.exists()) return 0;
        return file.length();
    }
    long sizeofFiles = 0;
    for (FileSystemNode fileOrDir : subNodes) {
         sizeofFiles += fileOrDir.countSizeOfFiles();
    }
    return sizeofFiles;

單純從功能實(shí)現(xiàn)角度來說,上面的代碼沒有問題,已經(jīng)實(shí)現(xiàn)了我們想要的功能。但是,如果我們開發(fā)的是一個(gè)大型系統(tǒng),從擴(kuò)展性(文件或目錄可能會(huì)對應(yīng)不同的操作)、業(yè)務(wù)建模(文件和目錄從業(yè)務(wù)上是兩個(gè)概念)、代碼的可讀性(文件和目錄區(qū)分對待更加符合人們對業(yè)務(wù)的認(rèn)知)的角度來說,我們最好對文件和目錄進(jìn)行區(qū)分設(shè)計(jì),定義為 File 和 Directory 兩個(gè)類。

按照這個(gè)設(shè)計(jì)思路,我們對代碼進(jìn)行重構(gòu)。重構(gòu)之后的代碼如下所示:

public abstract class FileSystemNode {
  protected String path;
  public FileSystemNode(String path) {
    this.path = path;
  }
  public abstract int countNumOfFiles();
  public abstract long countSizeOfFiles();
    public String getPath() {
    return path;
  }
}
public class File extends FileSystemNode {
  public File(String path) {
    super(path);
  }
  @Override
  public int countNumOfFiles() {
    return 1;
  }
  @Override
  public long countSizeOfFiles() {
    java.io.File file = new java.io.File(path);
    if (!file.exists()) return 0;
    return file.length();
  }
}
public class Directory extends FileSystemNode {
  private List<FileSystemNode> subNodes = new ArrayList<>();
  public Directory(String path) {
    super(path);
  }
  @Override
  public int countNumOfFiles() {
    int numOfFiles = 0;
    for (FileSystemNode fileOrDir : subNodes) {
      numOfFiles += fileOrDir.countNumOfFiles();
    }
    return numOfFiles;
  }
  @Override
  public long countSizeOfFiles() {
    long sizeofFiles = 0;
    for (FileSystemNode fileOrDir : subNodes) {
      sizeofFiles += fileOrDir.countSizeOfFiles();
    }
    return sizeofFiles;
  }
  public void addSubNode(FileSystemNode fileOrDir) {
    subNodes.add(fileOrDir);
  }
  public void removeSubNode(FileSystemNode fileOrDir) {
    int size = subNodes.size();
    int i = 0;
    for (; i < size; ++i) {
      if (subNodes.get(i).getPath().equalsIgnoreCase(fileOrDir.getPath())) {
        break;
      }
    }
    if (i < size) {
      subNodes.remove(i);
    }
  }
}

文件和目錄類都設(shè)計(jì)好了,我們來看,如何用它們來表示一個(gè)文件系統(tǒng)中的目錄樹結(jié)構(gòu)。具體的代碼示例如下所示:

public class Demo {
  public static void main(String[] args) {
    /**
     * /
     *
     * <p>/wz/
     *
     * <p>/wz/a.txt
     *
     * <p>/wz/b.txt
     *
     * <p>/wz/movies/
     *
     * <p>/wz/movies/c.avi
     *
     * <p>/xzg/
     *
     * <p>/xzg/docs/
     *
     * <p>/xzg/docs/d.txt
     */
    Directory fileSystemTree = new Directory("/");
    Directory node_wz = new Directory("/wz/");
    Directory node_xzg = new Directory("/xzg/");
    fileSystemTree.addSubNode(node_wz);
    fileSystemTree.addSubNode(node_xzg);    
    File node_wz_a = new File("/wz/a.txt");
    File node_wz_b = new File("/wz/b.txt");
    Directory node_wz_movies = new Directory("/wz/movies/");
    node_wz.addSubNode(node_wz_a);
    node_wz.addSubNode(node_wz_b);
    node_wz.addSubNode(node_wz_movies);    
    File node_wz_movies_c = new File("/wz/movies/c.avi");
    node_wz_movies.addSubNode(node_wz_movies_c);    
    Directory node_xzg_docs = new Directory("/xzg/docs/");
    node_xzg.addSubNode(node_xzg_docs);    
    File node_xzg_docs_d = new File("/xzg/docs/d.txt");
    node_xzg_docs.addSubNode(node_xzg_docs_d);    
    System.out.println("/ files num:" + fileSystemTree.countNumOfFiles());
    System.out.println("/wz/ files num:" + node_wz.countNumOfFiles());
  }
}

我們對照著這個(gè)例子,再重新看一下組合模式的定義:“將一組對象(文件和目錄)組織成樹形結(jié)構(gòu),以表示一種‘部分 - 整體’的層次結(jié)構(gòu)(目錄與子目錄的嵌套結(jié)構(gòu))。組合模式讓客戶端可以統(tǒng)一單個(gè)對象(文件)和組合對象(目錄)的處理邏輯(遞歸遍歷)。”

實(shí)際上,剛才講的這種組合模式的設(shè)計(jì)思路,與其說是一種設(shè)計(jì)模式,倒不如說是對業(yè)務(wù)場景的一種數(shù)據(jù)結(jié)構(gòu)和算法的抽象。其中,數(shù)據(jù)可以表示成樹這種數(shù)據(jù)結(jié)構(gòu),業(yè)務(wù)需求可以通過在樹上的遞歸遍歷算法來實(shí)現(xiàn)。

組合模式的應(yīng)用場景舉例

剛剛我們講了文件系統(tǒng)的例子,對于組合模式,我這里再舉一個(gè)例子。搞懂了這兩個(gè)例子,你基本上就算掌握了組合模式。在實(shí)際的項(xiàng)目中,遇到類似的可以表示成樹形結(jié)構(gòu)的業(yè)務(wù)場景,你只要“照葫蘆畫瓢”去設(shè)計(jì)就可以了。

假設(shè)我們在開發(fā)一個(gè) OA 系統(tǒng)(辦公自動(dòng)化系統(tǒng))。公司的組織結(jié)構(gòu)包含部門和員工兩種數(shù)據(jù)類型。其中,部門又可以包含子部門和員工。在數(shù)據(jù)庫中的表結(jié)構(gòu)如下所示:

我們希望在內(nèi)存中構(gòu)建整個(gè)公司的人員架構(gòu)圖(部門、子部門、員工的隸屬關(guān)系),并且提供接口計(jì)算出部門的薪資成本(隸屬于這個(gè)部門的所有員工的薪資和)。

部門包含子部門和員工,這是一種嵌套結(jié)構(gòu),可以表示成樹這種數(shù)據(jù)結(jié)構(gòu)。計(jì)算每個(gè)部門的薪資開支這樣一個(gè)需求,也可以通過在樹上的遍歷算法來實(shí)現(xiàn)。所以,從這個(gè)角度來看,這個(gè)應(yīng)用場景可以使用組合模式來設(shè)計(jì)和實(shí)現(xiàn)。

這個(gè)例子的代碼結(jié)構(gòu)跟上一個(gè)例子的很相似,代碼實(shí)現(xiàn)我直接貼在了下面,你可以對比著看一下。其中,HumanResource 是部門類(Department)和員工類(Employee)抽象出來的父類,為的是能統(tǒng)一薪資的處理邏輯。Demo 中的代碼負(fù)責(zé)從數(shù)據(jù)庫中讀取數(shù)據(jù)并在內(nèi)存中構(gòu)建組織架構(gòu)圖。

public  abstract  class  HumanResource  {
  protected  long id;
  protected  double salary;
  public  HumanResource(long id)  {
    this.id = id;
  }
  public  long  getId()  {
    return id;
  }
  public  abstract  double  calculateSalary();

}
public  class  Employee  extends  HumanResource  {
  public  Employee(long id, double salary)  {
    super(id);
    this.salary = salary;
  }
  @Override
  public  double  calculateSalary()  {
    return salary;
  }
}
public  class  Department  extends  HumanResource  {
  private List<HumanResource> subNodes = new ArrayList<>();
  public  Department(long id)  {
    super(id);
  }
  @Override
  public  double  calculateSalary()  {
    double totalSalary = 0;
    for (HumanResource hr : subNodes) {
      totalSalary += hr.calculateSalary();
    }
    this.salary = totalSalary;
    return totalSalary;
  }
  public  void  addSubNode(HumanResource hr)  {
    subNodes.add(hr);
  }
}
// 構(gòu)建組織架構(gòu)的代碼
public  class  Demo  {
  private  static  final  long ORGANIZATION\_ROOT\_ID = 1001;
  private DepartmentRepo departmentRepo; // 依賴注入
  private EmployeeRepo employeeRepo; // 依賴注入
  public  void  buildOrganization()  {
    Department rootDepartment = new Department(ORGANIZATION\_ROOT\_ID);
    buildOrganization(rootDepartment);
  }
  private  void  buildOrganization(Department department)  {
    List<Long> subDepartmentIds = departmentRepo.getSubDepartmentIds(department.getId());
    for (Long subDepartmentId : subDepartmentIds) {
      Department subDepartment = new Department(subDepartmentId);
      department.addSubNode(subDepartment);
      buildOrganization(subDepartment);
    }
    List<Long> employeeIds = employeeRepo.getDepartmentEmployeeIds(department.getId());
    for (Long employeeId : employeeIds) {
      double salary = employeeRepo.getEmployeeSalary(employeeId);
      department.addSubNode(new Employee(employeeId, salary));
    }
  }
}

我們再拿組合模式的定義跟這個(gè)例子對照一下:“將一組對象(員工和部門)組織成樹形結(jié)構(gòu),以表示一種‘部分 - 整體’的層次結(jié)構(gòu)(部門與子部門的嵌套結(jié)構(gòu))。組合模式讓客戶端可以統(tǒng)一單個(gè)對象(員工)和組合對象(部門)的處理邏輯(遞歸遍歷)。

提交申請后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)

免費(fèi)課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 欧美亚洲国产第一页草草 | 天天做天天爱夜夜爽女人爽宅 | 日本高清有码 | 亚洲国产欧洲 | 寡妇野外啪啪一区二区 | 天天干天天操天天爱 | 日本一本在线观看 | 四虎网址最新 | 久久国产美女免费观看精品 | 99国产精品久久久久久久... | 欧美激情亚洲精品日韩1区2区 | 精品久久视频 | 日本波多野结衣在线 | 99热久久精里都是精品66 | 天天插天天插 | 七次郎在线成人精品 | 亚洲四虎 | 五月天激情视频在线观看 | 日韩视频 中文字幕 视频一区 | 99热在线获取最新地址 | 青青久久久国产线免观 | 中文字幕第一页国产 | 久久青草免费线观最新 | 欧美国产成人一区二区三区 | 亚洲国产精品欧美日韩一区二区 | 超碰v| 嫩草视频在线观看 | 国产成人高清视频免费播放 | 久久成人18 | 天天在线欧美精品免费看 | 国产免费人人看大香伊 | 99热这里只有精品第一页 | 成人黄色网址 | 欧洲欧美成人免费大片 | 一级做a免费视频观看网站 一级做a爰片久久毛片唾 | 国产小视频在线观看 | www.国产福利视频.com | 日韩麻豆国产精品欧美 | 91免费精品国自产拍在线不卡 | 久久婷婷五综合一区二区 | 日本免费人做人一区在线观看 |