趣談設計模式——組合模式

組合模式的定義

Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.
將對象組合成樹形結構以表示「部分-總體」的層次結構,使得用戶對某個對象和組合對象的使用具備一致性。

案例

這句話看起來彷佛不太好理解,咱們能夠暫時先有一個概念。接下來咱們會經過具體的案例,來直觀的感覺什麼是組合模式。
假如你是一下小型IT公司的老闆,整個公司處於初創階段,只有4個部門,每一個部門各司其職,負責對外承接研發工做。
--金融
--文娛
--旅遊
--海淘
你的任務,就是將具體的需求分發給不一樣的部門,因此你很容易寫出了這樣一段代碼。
if(type == 金融){
    //..
}else if(type == 文娛){
    //..
}
else if(type == 旅遊){
    //..
}else{
    //..
}
通過你的不斷努力,一段時間事後,公司已經初具規模,這個時候公司的體系架構也變得複雜起來:
公司分爲不一樣的事業羣、每一個事業羣下有不一樣的戰區、每一個戰區下又有一級部門、一級部門下有二級部門等等等等...,這個時候,你會發現,若是你還按照原來的方法去編寫代碼,你的代碼可能會變成這樣:
(這種代碼又叫作箭頭形代碼,有不少優化方法,好比提取方法,提早return..等等,往後會出一個代碼規範化、優化的博客)
回過頭來,咱們再來分析這個問題,咱們但願的是,不一樣部門,各司其職,這個時候咱們就能夠考慮採用 組合模式
每一個部門其實都是由一個個子部門組成,子部門和部門的關係,其實就是咱們定義中所說的「總體-部分」的關係,若是咱們把公司看成一顆樹的根節點,那麼各個部門就組成了這顆樹的枝和葉,這樣部門之間就有了樹的層級結構。
而定義中描述的:使得用戶對某個對象和組合對象的使用具備一致性,其實就是咱們能夠經過協議(編程語言中的抽象類或接口)來定義部門的屬性和行爲,這樣不管是對於哪一個部門,他對外的操做都是一致的,總體和部分就具備了一致性。

這樣作有什麼好處?

首先總體是由部分組成的, 每一個部分其實能夠做爲其部分的總體,遞歸下去,因此咱們代碼中調用部分的地方,均可以用總體來替換。
(好比CTO體系/企業應用研發部/企業信息化部,CTO體系是一個總體,企業應用研發部是部分,可是對於企業信息化部來講,企業應用研發部其實也是一個總體)
其次若是總體和部分具備一致性,那麼對於咱們來講,將不用去書寫代碼去區分不一樣的部分了,而是能夠以一種統一的方式去處理。

實現模型:

組合模式在代碼具體實現上,有兩種不一樣的方式:
1.透明組合模式:把組合(樹節點)使用的方法放到統一行爲(Component)中,讓不一樣層次(樹節點,葉子節點)的結構都具有一致行爲;
2.安全組合模式:統一行爲(Component)只規定系統各個層次的最基礎的一致行爲,而把組合(樹節點)自己的方法(管理子類對象的添加,刪除等)放到自身當中;

爲何會有兩種實現模型呢?

首先就是透明模式,會引入沒必要要的依賴,好比葉子結點實際上是不需備add和remove功能的。因此纔有了安全模式,將節點自己的方法放到自身維護,這樣作的缺點就是各個節點間是有差別的,沒有辦法用徹底統一的方式去處理。雖然實現不一樣,可是它們都遵循組合模式的規則。

對於兩種模式的思考

其實組合模式是一種反範式的設計模式,好比透明模式會引入沒必要要依賴的行爲,違反了接口隔離原則, 安全模式區分了葉子結點和樹枝節點,致使客戶端沒有辦法依賴抽象,違反了依賴倒置原則,基類也不能用子類去替換,違反了裏式替換原則。

代碼實現

接下來咱們來實現透明模式組合模式的代碼:
/**
 * 組合模式協議.
 *
 * @author jialin.li
 * @date 2019-12-20 15:22
 */
public abstract class AbstractComponent {
    String name;

    public AbstractComponent(String name) {
        this.name = name;
    }

    public abstract void add(AbstractComponent c);
    public abstract void remove(AbstractComponent c);
    public abstract void display(int depth);
}
import java.util.ArrayList;
import java.util.List;

/**
 * 組合模式中的枝葉,用來存儲子部件.
 *
 * @author jialin.li
 * @date 2019-12-20 15:28
 */
public class Composite extends AbstractComponent {
    private List<AbstractComponent> children = new ArrayList<>();

    public Composite(String name) {
        super(name);
    }

    @Override
    public void add(AbstractComponent c) {
        children.add(c);
    }

    @Override
    public void remove(AbstractComponent c) {
        children.remove(c);
    }

    @Override
    public void display(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);
        for (AbstractComponent component : children) {
            component.display(depth + 1);
        }
    }
}
/**
 * 組合模式中的葉子節點.
 *
 * @author jialin.li
 * @date 2019-12-20 15:26
 */
public class Leaf extends AbstractComponent {

    public Leaf(String name) {
        super(name);
    }

    @Override
    public void add(AbstractComponent c) {
        System.out.println("Cannot add a leaf!");
    }

    @Override
    public void remove(AbstractComponent c) {
        System.out.println("Cannot remove a leaf!");
    }

    @Override
    public void display(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);
    }
}
/**
 * 客戶端.
 *
 * @author jialin.li
 * @date 2019-12-20 15:33
 */
public class Main {
    public static void main(String[] args) {
        Composite root = new Composite("root");
        root.add(new Leaf("Leaf A"));
        root.add(new Leaf("Leaf B"));

        Composite comp = new Composite("Composite X");
        comp.add(new Leaf("Leaf XA"));
        comp.add(new Leaf("Leaf XB"));

        root.add(comp);

        Composite comp2 = new Composite("Composite XY");
        comp2.add(new Leaf("Leaf XYA"));
        comp2.add(new Leaf("Leaf XYB"));
        comp.add(comp2);

        root.add(new Leaf("Leaf C"));
        Leaf d = new Leaf("D");
        root.add(d);
        root.remove(d);

        root.display(1);
    }
}

執行結果

-root
--Leaf A
--Leaf B
--Composite X
---Leaf XA
---Leaf XB
---Composite XY
----Leaf XYA
----Leaf XYB
--Leaf C

Tomcat中的組合模式

tomcat中的容器設計,也用到了組合模式:
tomcat中設計了4中容器: Engine、Host、Context、Wrapper。這四種容器的關係以下:
它們之間是父子關係,除了Engine容器,其餘均可以具備多個。容器是一個樹狀的層級結構
engine表示引擎,一個Service最多隻能有一個引擎,Host表示虛擬主機、Context表示一個Web應用、Wrapper表示一個Servlet(後三種容器能夠有多個)
Tomcat 的server.xml配置文件中的標籤,其實就表明了tomcat的層級結構:
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>
  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>
    </Engine>
  </Service>
</Server>
這四種容器都符合一個協議:
public interface Container extends Lifecycle {
    public void setName(String name);
    public Container getParent();
    public void setParent(Container container);
    public void addChild(Container child);
    public void removeChild(Container child);
    public Container findChild(String name);
}
咱們能夠看到該接口具備 getParent、setParent、addChild 和 removeChild 等方法,是一種透明組合模式。
相關文章
相關標籤/搜索