文章首發: "結構型模式:組合模式" 七大結構型模式之三:組合模式。 簡介 姓名 :組合模式 英文名 :Composite Pattern 價值觀 :專門解決各種樹形疑難雜症 個人介紹 : Compose objects into tree structures to represent part ...
文章首發:
結構型模式:組合模式
七大結構型模式之三:組合模式。
簡介
姓名 :組合模式
英文名 :Composite Pattern
價值觀 :專門解決各種樹形疑難雜症
個人介紹 :
Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.
將對象組合成樹形結構以表示“部分-整體”的層次結構,使得用戶對單個對象和組合對象的使用具有一致性。
(來自《設計模式之禪》)
你要的故事
今天咱們再講講咱們程式猿的組織架構。技術類的組織架構比較單一,基本上都是這樣:經理--->組長--->工程師,如下圖所示。
各個公司的 title 可能不太一樣,但是基本是差不多這種架構,按職業發展,從入職到能獨立開發需求便為工程師,從獨立開發需求到能帶小團隊開發便為組長,從帶小團隊開發到能帶幾個團隊一起協作開發便為經理。
假設目前有一家公司,技術部就 4 個人,大熊擔任經理,中熊擔任組長,小熊1和小熊2擔任工程師。下麵的代碼都圍繞這個假設編寫。
非組合模式
我們先來一個非正常的實現方案:從組織架構里,有 3 個角色,分別是經理、組長、工程師,那麼我們就按角色去實現一番。
Manager 為經理類,經理下有多個組長 leaders。
/**
* 經理
*/
class Manager {
private String name;
private List<Leader> leaders;
public Manager(String name) {
this.name = name;
this.leaders = new LinkedList<>();
}
public void add(Leader leader) {
this.leaders.add(leader);
}
public void remove(Leader leader) {
this.leaders.remove(leader);
}
public void display(int index) {
for (int i = 0; i < index; i++) {
System.out.print("----");
}
System.out.println("經理:" + this.name);
leaders.forEach(leader -> {
leader.display(index+1);
});
}
}
Leader 為組長類,組長下有多個工程師 engineers。
/**
* 組長
*/
class Leader {
private String name;
private List<Engineer> engineers;
public Leader(String name) {
this.name = name;
this.engineers = new LinkedList<>();
}
public void add(Engineer engineer) {
this.engineers.add(engineer);
}
public void remove(Engineer engineer) {
this.engineers.remove(engineer);
}
public void display(int index) {
for (int i = 0; i < index; i++) {
System.out.print("----");
}
System.out.println("組長:" + this.name);
engineers.forEach(engineer -> {
engineer.display(index + 1);
});
}
}
Engineer 為工程師類,工程師沒有下屬。
/**
* 工程師
*/
class Engineer {
private String name;
public Engineer(String name) {
this.name = name;
}
public void display(int index) {
for (int i = 0; i < index; i++) {
System.out.print("----");
}
System.out.println("工程師:" + this.name);
}
}
測試代碼
public class NoCompositeTest {
public static void main(String[] args) {
Manager manager = new Manager("大熊");
Leader leader = new Leader("中熊");
Engineer engineer1= new Engineer("小熊1");
Engineer engineer2 = new Engineer("小熊2");
manager.add(leader);
leader.add(engineer1);
leader.add(engineer2);
manager.display(0);
}
}
列印結果:
經理:大熊
----組長:中熊
--------工程師:小熊1
--------工程師:小熊2
這份代碼看完之後,有什麼想法?是不是感覺代碼有點冗餘?經理和組長的代碼幾乎一致,而工程師類和經理類、組長類也有共同點,唯一的區別就是工程師沒有下屬,因此沒有對下屬的增刪操作方法。
安全模式
通過上面一層思考,這 3 個角色有相通性,我們可以抽象出一個 Employee2 類,把 3 個角色共同的特性放到 Employee2 類中,經理和組長合併共用一個類,因為在這個例子里,這 2 個角色完全一樣的。下麵看代碼。
Employee2 抽象類,它有這 3 個角色共有的特性,名稱設置獲取以及顯示數據。
abstract class Employee2 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void display(int index);
}
Leader2 領導類,把上面的經理類和組長類都合併到這個領導類,因為他們都是領導層。
class Leader2 extends Employee2 {
private List<Employee2> employees;
public Leader2(String name) {
this.setName(name);
this.employees = new ArrayList<>();
}
public void add(Employee2 employee) {
this.employees.add(employee);
}
public void remove(Employee2 employee) {
this.employees.remove(employee);
}
@Override
public void display(int index) {
for(int i = 0; i < index; i++) {
System.out.print("----");
}
System.out.println("領導:" + this.getName());
this.employees.forEach(employee -> {
employee.display(index + 1);
});
}
}
Engineer2 工程師類,工程師類比較簡單,因為名稱設置獲取在抽象類 Employee2 有了,所以就只需實現顯示數據的功能。
class Engineer2 extends Employee2 {
public Engineer2(String name) {
this.setName(name);
}
@Override
public void display(int index) {
for(int i = 0; i < index; i++) {
System.out.print("----");
}
System.out.println("工程師:" + this.getName());
}
}
測試代碼
public class CompositeTest {
public static void main(String[] args) {
// 安全模式
Leader2 leader1 = new Leader2("大熊");
Leader2 leader2 = new Leader2("中熊");
Engineer2 engineer1 = new Engineer2("小熊1");
Engineer2 engineer2 = new Engineer2("小熊2");
leader1.add(leader2);
leader2.add(engineer1);
leader2.add(engineer2);
leader1.display(0);
}
}
列印結果:
領導:大熊
----領導:中熊
--------工程師:小熊1
--------工程師:小熊2
看下運行結果和上面是一致的,這份代碼比第一份代碼有更好的封裝性,也更符合面向對象的編程方式,經理和組長被合併成 Leader2,也就是咱們今天講的組合模式,Leader2 為組合對象。上面講的是安全模式,安全模式指的是抽象類 Employee2 只提供了 3 個角色中共有的特性,安全是相對透明模式所說的,因為這裡領導類 Leader2 和工程師類 Engineer2 都只提供了自己能提供的方法,Engineer2 不會有多餘的方法,而透明模式則不是。下麵講講透明模式。
透明模式
透明模式把組合對象(即領導類)使用的方法放到抽象類中,而因為工程師沒有下屬,則不具體實現對應的方法。代碼如下。
Employee3 抽象類,將組合對象的屬性 employees
和 方法 add()
、 remove()
都放到這個類裡面。
abstract class Employee3 {
private String name;
private List<Employee3> employees;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Employee3> getEmployees() {
return employees;
}
public void setEmployees(List<Employee3> employees) {
this.employees = employees;
}
public abstract void add(Employee3 employee);
public abstract void remove(Employee3 employee);
public abstract void display(int index);
}
Leader3 領導類,具體實現 Employee3 提供的所有方法。
class Leader3 extends Employee3 {
public Leader3(String name) {
this.setName(name);
this.setEmployees(new ArrayList<>());
}
@Override
public void add(Employee3 employee) {
this.getEmployees().add(employee);
}
@Override
public void remove(Employee3 employee) {
this.getEmployees().remove(employee);
}
@Override
public void display(int index) {
for(int i = 0; i < index; i++) {
System.out.print("----");
}
System.out.println("領導:" + this.getName());
this.getEmployees().forEach(employee -> {
employee.display(index + 1);
});
}
}
Engineer3 工程師類,只具體實現 Employee3 中的 display()
方法,add()
和 remove()
方法不是工程師具備的,所以留空,不做具體實現。
class Engineer3 extends Employee3 {
public Engineer3(String name) {
this.setName(name);
}
@Override
public void add(Employee3 employee) {
// 沒有下屬
}
@Override
public void remove(Employee3 employee) {
// 沒有下屬
}
@Override
public void display(int index) {
for(int i = 0; i < index; i++) {
System.out.print("----");
}
System.out.println("工程師:" + this.getName());
}
}
測試代碼:
public class CompositeTest {
public static void main(String[] args) {
// 透明模式
Leader3 leader3 = new Leader3("大熊");
Leader3 leader31 = new Leader3("中熊");
Engineer3 engineer31 = new Engineer3("小熊1");
Engineer3 engineer32 = new Engineer3("小熊2");
leader3.add(leader31);
leader31.add(engineer31);
leader31.add(engineer32);
leader3.display(0);
}
列印結果:
領導:大熊
----領導:中熊
--------工程師:小熊1
--------工程師:小熊2
}
安全模式把 3 個角色的共同點抽象到 Employee2 中,透明模式則把 3 個角色中的領導者(組合對象)的內容抽象到 Employee3 中。透明模式有些不好的地方在於工程師也有領導者的下屬對象和相應的方法,其實工程師並沒有這些功能。安全模式把領導者和工程師分開,每個對象都只提供自己具有的功能,這樣子在使用的時候也就更安全。
總結
我們根據 IT 組織架構,從簡單的每個角色對應一個類的實現,再到抽象出每個角色共同的功能、組合領導類的安全模式,接著再到抽象起來領導類(組合)所有功能的透明模式,分析了組合模式的完整過程,也講了安全模式和透明模式的差異。組合模式讓對象更加有層次,將對象的劃分更加清晰,特別是樹形結構的層次,利用組合模式會更加簡化。
推薦閱讀
公眾號後臺回覆『大禮包』獲取 Java、Python、IOS 等教程
加個人微信備註『教程』獲取架構師、機器學習等教程