デザインモード[コンビネーションモード]、ツリー構造の完璧なソリューション

1. コンビネーションモードとは

複合パターンは、部分全体パターンとも呼ばれます。その目的は、単一のオブジェクト (葉ノード) と複合オブジェクト (小枝ノード) を同じインターフェイスで表現し、顧客が複合オブジェクトの使用と一致するようにすることです。オブジェクトであり、構造モードに属します。

Composite パターンは、树形结构オブジェクトの観点からオブジェクトを構成し、階層の一部と全体を表します。`

GOF24 原文: オブジェクトをツリー構造に結合して「部分全体」の階層を表現すると、ユーザーは個々のオブジェクトと複合オブジェクトの使用に一貫性を持たせることができます。

組み合わせモードの設計アイデアは、設計モードというよりも、ビジネス シナリオ向けのデータ構造とアルゴリズムを抽象化したものです。このうち、データはツリーなどのデータ構造として表現でき、ツリー上の再帰的走査アルゴリズムを通じてビジネス要件を実現できます。結合モードでは、オブジェクトのグループをツリー構造に編成し、個々のオブジェクトと複合オブジェクトの両方をツリー内のノードとして扱い、処理ロジックを統合します。また、ツリー構造の特性を使用して各サブツリーを再帰的に処理し、コードを簡素化します。使用组合模式的前提在于,你的业务场景必须能够表示成树形结构。所以,组合模式的应用场景也比较局限,它并不是一种很常用的设计模式

1. コンビネーションモードでの3つの役割

結合モードは通常、全体と部分の関係を記述するために使用されます。オブジェクトをツリー構造に編成します。最上位のノードはルート ノードと呼ばれます。ルート ノードにはブランチ ノードとリーフ ノードを含めることができ、ブランチ ノードには次のことができます。ブランチ ノードとリーフ ノードが含まれます。以下の図に示すように:
ここに画像の説明を挿入
上の図からわかるように、ルート ノードとブランチ ノードは本質的に同じデータ型であり、コンテナとして使用できます。一方、リーフ ノードとブランチ ノードは同じではありません。意味的には型ですが、 の組み合わせモードでは、ブランチ ノードとリーフ ノードは同じデータ型 (同じインターフェイスで定義された) とみなされ、一貫した動作が行われます。このように、結合モードでは、ツリー構造全体のオブジェクトが同じ種類となるため、お客様は枝ノード、葉ノードを区別する必要がなく、直接操作できるというメリットがあり、お客様にとって大きなメリットとなります。お客様にとって便利です。

組み合わせモードには 3 つの役割が含まれます。

  • 抽象ルート ノード (コンポーネント): システムのすべてのレベルでオブジェクトの共通メソッドとプロパティを定義し、いくつかのデフォルトの動作とプロパティを事前定義できます。
  • ブランチ ノード (複合): ブランチ ノードの動作を定義し、子ノードを保存し、ブランチ ノードとリーフ ノードを組み合わせてツリー構造を形成します。
  • リーフノード (Leaf): リーフノードオブジェクト。その下に分岐はなく、システム階層横断の最小単位です。

コードで組み合わせモードを実装するには 2 つの異なる方法があります透明组合模式和安全组合模式

2. コンビネーションモードの適用シナリオ

サブシステムとそのオブジェクト レベルがツリー構造を表す場合、合成モードを使用して、サブシステム内の各オブジェクト レベルの動作と操作を一貫させることができます。クライアントがサブシステム内の階層オブジェクトを使用する場合、区別する必要はなく、一般的な操作をそのまま使用できるため、クライアントの使用が便利になります。

(1) クライアントが結合オブジェクトと単一オブジェクトの違いを無視できることが望ましい場合
(2) オブジェクトの階層は全体と部分があり、ツリー構造になっています。

一般的に使用される組み合わせモードのシナリオ:
ツリー メニュー、オペレーティング システムのディレクトリ構造、会社の組織構造など。
ここに画像の説明を挿入

3. コンビネーションモードの注意事項と詳細

(1) クライアントの操作を簡素化します。クライアントは、部分全体やノードの葉を考慮せずに、一貫したオブジェクトのみを処理する必要があります。
(2) 拡張性が強い。複合オブジェクトを変更したい場合、内部の階層関係を調整するだけでよく、クライアントは変更を加える必要がありません。
(3) 複雑な階層構造を作成するのに便利です。クライアントは組み合わせの構成の詳細を気にする必要がなく、ノードやリーフを簡単に追加して複雑なツリー構造を作成できます。
(4) 結合モードは、組織を横断したり、ツリー構造でオブジェクトを処理したりするのに非常に適しています。(5)たとえば、多くのメソッドと属性が異なる
場合、より高度な抽象化が必要です节点和叶子有很多差异性的话不适合使用组合模式

2. 透明結合モード

透過合成モードでは、すべてのパブリック メソッドをコンポーネントで定義します。このモードの利点は、クライアントがリーフ ノード (Leaf) とブランチ ノード (Composite) を区別する必要がなく、完全に一貫したインターフェイスを持つことです。その UML クラス図は次のとおりです。
ここに画像の説明を挿入

1. 大学・学部の事例

学校の学部構成を例に挙げると、学校には複数のカレッジがあり、カレッジには複数の学部があります。

まず、抽象ルート ノードを定義します。

// 抽象根节点
public abstract class OrganizationComponent {
    
    

	private String name; // 名字
	private String des; // 说明
	
	protected  void add(OrganizationComponent organizationComponent) {
    
    
		//默认实现
		throw new UnsupportedOperationException();
	}
	
	protected  void remove(OrganizationComponent organizationComponent) {
    
    
		//默认实现
		throw new UnsupportedOperationException();
	}

	//构造器
	public OrganizationComponent(String name, String des) {
    
    
		super();
		this.name = name;
		this.des = des;
	}
	// 省略 get  set
	
	//方法print, 做成抽象的, 子类都需要实现
	protected abstract void print();
	
}

大学:

//University 就是 Composite , 可以管理College
public class University extends OrganizationComponent {
    
    
	// 定义学院集合 College
	List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

	// 构造器
	public University(String name, String des) {
    
    
		super(name, des);
	}

	// 重写add
	@Override
	protected void add(OrganizationComponent organizationComponent) {
    
    
		organizationComponents.add(organizationComponent);
	}

	// 重写remove
	@Override
	protected void remove(OrganizationComponent organizationComponent) {
    
    
		organizationComponents.remove(organizationComponent);
	}

	@Override
	public String getName() {
    
    
		return super.getName();
	}

	@Override
	public String getDes() {
    
    
		return super.getDes();
	}

	// print方法,就是输出University 包含的学院
	@Override
	protected void print() {
    
    
		System.out.println("--------------" + getName() + "--------------");
		//遍历 organizationComponents 
		for (OrganizationComponent organizationComponent : organizationComponents) {
    
    
			organizationComponent.print();
		}
	}
}

カレッジ:

public class College extends OrganizationComponent {
    
    

	//List 中 存放的Department
	List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

	// 构造器
	public College(String name, String des) {
    
    
		super(name, des);
	}

	// 重写add
	@Override
	protected void add(OrganizationComponent organizationComponent) {
    
    
		//  将来实际业务中,Colleage 的 add 和  University add 不一定完全一样
		organizationComponents.add(organizationComponent);
	}

	// 重写remove
	@Override
	protected void remove(OrganizationComponent organizationComponent) {
    
    
		organizationComponents.remove(organizationComponent);
	}

	@Override
	public String getName() {
    
    
		return super.getName();
	}

	@Override
	public String getDes() {
    
    
		return super.getDes();
	}

	// print方法,就是输出University 包含的学院
	@Override
	protected void print() {
    
    
		System.out.println("--------------" + getName() + "--------------");
		//遍历 organizationComponents 
		for (OrganizationComponent organizationComponent : organizationComponents) {
    
    
			organizationComponent.print();
		}
	}
}

メジャー (リーフ ノード):

public class Department extends OrganizationComponent {
    
    

	//没有集合
	
	public Department(String name, String des) {
    
    
		super(name, des);
	}

	//add , remove 就不用写了,因为他是叶子节点
	@Override
	public String getName() {
    
    
		return super.getName();
	}
	
	@Override
	public String getDes() {
    
    
		return super.getDes();
	}
	
	@Override
	protected void print() {
    
    
		System.out.println(getName());
	}
}

テストクラス:

public class Client {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		
		//从大到小创建对象 学校
		OrganizationComponent university = new University("清华大学", " 中国顶级大学 ");
		
		//创建 学院
		OrganizationComponent computerCollege = new College("计算机学院", " 计算机学院 ");
		OrganizationComponent infoEngineercollege = new College("信息工程学院", " 信息工程学院 ");
		
		
		//创建各个学院下面的系(专业)
		computerCollege.add(new Department("软件工程", " 软件工程不错 "));
		computerCollege.add(new Department("网络工程", " 网络工程不错 "));
		computerCollege.add(new Department("计算机科学与技术", " 计算机科学与技术是老牌的专业 "));
		
		//
		infoEngineercollege.add(new Department("通信工程", " 通信工程不好学 "));
		infoEngineercollege.add(new Department("信息工程", " 信息工程好学 "));
		
		//将学院加入到 学校
		university.add(computerCollege);
		university.add(infoEngineercollege);
		
		//university.print();
		university.print();
	}

}

最終的な UML クラス図は次のとおりです。
ここに画像の説明を挿入

2. 透過合成モードの概要

透過的な合成モードでは、コンポーネント内のすべてのパブリック メソッドが定義されます。このモードの利点は、クライアントがまったく同じインターフェイスを持つリーフ ノード (Leaf) とブランチ ノード (Composite) を区別する必要がないことです。欠点は、次のとおりです。リーフ ノード (Leaf) は、継承によって必要のないメソッド (サブクラスの操作を管理するメソッド) を取得します。これは、デザイン パターンのインターフェイス分離原則に違反します。

3. 証券組み合わせモード

セキュリティ構成モードは、システムの各レベルの最も基本的な一貫した動作を規定するだけであり、構成(ツリーノード)自体のメソッド(サブクラスオブジェクトの追加や削除の管理など)はそれ自体に組み込まれます。その UML クラス図は次のとおりです。
ここに画像の説明を挿入

1. Linuxディレクトリシステムの場合

ファイル システムには、フォルダーとファイルの 2 つの層があります。このうち、ファイルを保持できるフォルダーはブランチ ノードであり、ファイルはリーフ ノードです。ディレクトリ システムの階層は少なく、ブランチ ノード (フォルダー) の構造は比較的安定しており、ファイルには実際に多くの種類を含めることができるため、ここではセキュリティ組み合わせモードを使用してディレクトリ システムを実装することを選択します。これにより、ディレクトリ システムの冗長性の導入を回避できます。リーフタイプ (ファイル) メソッド。

// 顶层抽象
public abstract class Directory {
    
    

    protected String name;

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

    public abstract void show();
}
// 目录
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>();
    }

    @Override
    public 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 ++){
    
    
                    //每一行开始打印一个+号
                    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);
        }
    }

}
// 文件
public class File extends Directory {
    
    

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

    @Override
    public void show() {
    
    
        System.out.println(this.name);
    }

}
// 测试类
class Test {
    
    
    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();
    }
}

UML クラス図を見てみましょう。
ここに画像の説明を挿入

2. 証券併用モードの概要

セキュリティ複合モードの利点は、インターフェイス定義の責任が明確であり、設計モードの単一責任原則とインターフェイス分離原則に準拠していることですが、欠点は、顧客がブランチ ノード (複合ノード) を区別する必要があることです。 ) とリーフ ノード (Leaf) は、各レベルの操作を正しく処理するために、クライアントは抽象化 (コンポーネント) に依存できず、デザイン パターンの依存関係逆転の原則に違反します。

4 番目に、ソース コードで使用される組み合わせモード

1、ハッシュマップ

Map は抽象構造 (同時に、この構造はキーと値のペアの格納形式のみをサポートします) ですが、HashMap は中間構造であり、HashMap の Node ノードはリーフ ノードです。

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    
    
    // ...
	transient Node<K,V>[] table;
	// ...
}	

static class Node<K,V> implements Map.Entry<K,V> {
    
    
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
}

putAll メソッドの入力パラメータは Map です。

public void putAll(Map<? extends K, ? extends V> m) {
    
    
    putMapEntries(m, true);
}

2、配列リスト

ArrayList の addAll メソッド、そのパラメータは親クラス Collection でもあります。

public boolean addAll(Collection<? extends E> c) {
    
    
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

3、マイバティス

MyBatis はさまざまなマッピング ファイル内の SQL を解析し、S​​qlNode と呼ばれる非常に重要なクラスを設計します。XML 内の各ノードは SqlNode オブジェクトに解析され、最終的にすべての SqlNode がまとめられて完全な SQL ステートメントが形成されます。レベルデザインは非常にシンプルです。ソースコードを見てください:

public interface SqlNode {
    
    
  boolean apply(DynamicContext context);
}

apply メソッドは、渡されたパラメーター コンテキストに従って SqlNode によって記録された SQL フラグメントを分析し、DynamicContext.appendSql() メソッドを呼び出して、解析された SQL フラグメントを DynamicContext の SqlBuilder に追加して保存します。SQL ノードの下のすべての SqlNode の解析が完了すると、DynamicContext.getSql() を通じて完全な SQL ステートメントを取得できます。

クラス図を見てみましょう。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/A_art_xiang/article/details/130606421
おすすめ