1はじめに
実生活では、大学の学部や大学、本社の学部や支部、学用品の本やランドセル、日用品の洋服やワードローブ、鍋やフライパンなど、多くの「全体」の関係があります。キッチン。同じことがソフトウェア開発にも当てはまります。たとえば、ファイルシステム内のファイルとフォルダ、フォームプログラム内の単純なコントロールとコンテナコントロールなどです。これらの単純なオブジェクトと複合オブジェクトの処理は、結合モードを使用して実現する場合に非常に便利です。
2.コンビネーションモードの定義と特徴
複合パターンの定義:部分全体モードと呼ばれることもあります。これは、オブジェクトをツリーのような階層構造に結合して「全体」の関係を表現するパターンです。これにより、ユーザーは単一のオブジェクトに一貫してアクセスし、結合することができます。構造設計パターンであるオブジェクト。
組み合わせモードは、通常、全体と部分の関係を表すために使用されます。オブジェクトをツリー構造に編成します。最上位ノードはルートノードと呼ばれます。ルートノードにはブランチノードとリーフノードを含めることができ、ブランチノードには次のことができます。ブランチノードとリーフノードを含み、ツリー構造図は次のとおりです。
上の図からわかるように、実際には、ルートノードとブランチノードは本質的に同じデータ型であり、コンテナとして使用できますが、リーフノードとブランチノードは意味的に同じタイプに属していません。ただし、結合モードでは、ブランチノードとリーフノードは同じデータ型(統合インターフェイスで定義)に属していると見なされるため、動作は一貫しています。
このように、結合モードでは、ツリー構造全体のオブジェクトが同じタイプに属し、ユーザーがブランチノードかリーフノードかを区別する必要がなく、直接操作できるという利点があります。ユーザーに大きなメリットをもたらします。便利です。
結合モデルの主な利点は次のとおりです。
- 結合モードを使用すると、クライアントコードは、単一オブジェクトであるか結合オブジェクトであるかを気にすることなく、単一オブジェクトと結合オブジェクトを一貫して処理できます。これにより、クライアントコードが簡素化されます。
- アセンブリに新しいオブジェクトを追加する方が簡単であり、「開閉の原則」を満たす新しいオブジェクトが追加されているため、クライアントはソースコードを変更しません。
主な欠点は次のとおりです。
- 設計はより複雑であり、クライアントはクラス間の階層関係を明確にするためにより多くの時間を費やす必要があります
- コンテナ内のコンポーネントを制限するのは簡単ではありません
- 継承メソッドを使用してコンポーネントの新機能を追加するのは簡単ではありません
3.複合モードの構造と実現
結合モードの構造はそれほど複雑ではなく、その構造と実装を以下で分析します。
パターンの構造
組み合わせモードには、次の主な役割が含まれます。
- 抽象コンポーネント(コンポーネント)の役割:その主な機能は、リーフコンポーネントとブランチコンポーネントのパブリックインターフェイスを宣言し、それらのデフォルトの動作を実装することです。透過的構成モードでは、抽象コンポーネントはサブクラスにアクセスして管理するためのインターフェースも宣言します。安全構成モードでは、サブクラスにアクセスして管理するためのインターフェースは宣言されず、管理作業はブランチコンポーネントによって行われます。(全体の抽象クラスまたはインターフェースは、追加や削除など、いくつかの一般的なメソッドを定義します)
- リーフコンポーネント(リーフ)の役割:組み合わせのリーフノードオブジェクトであり、子ノードはありません。抽象コンポーネントを継承または実装するために使用されます。
- ブランチコンポーネント(複合)ロール/中間コンポーネント:これは、組み合わせのブランチノードオブジェクトであり、子ノードがあり、抽象コンポーネントを継承および実装するために使用されます。その主な機能は、通常Add()、Remove()、GetChild()およびその他のメソッドを含むサブコンポーネントを格納および管理することです。
コンビネーションモードは、トランスペアレントコンビネーションモードとセーフコンビネーションモードに分かれています。
- 透明な方法
このように、抽象コンポーネントはすべてのサブクラスのすべてのメソッドを宣言するため、クライアントは、クライアントに対して透過的なリーフオブジェクトとブランチオブジェクトを区別する必要がありません。ただし、その欠点は次のとおりです。リーフコンポーネントには元々Add()、Remove()、およびGetChild()メソッドがありませんが、実装する必要があり(空の実装または例外のスロー)、セキュリティ上の問題が発生します。構造図を図1に示します。
- 安全な方法
このように、サブコンポーネントの管理方法はブランチコンポーネントに移動します。抽象コンポーネントとリーフコンポーネントにはサブオブジェクトの管理方法がありません。これにより、前の方法のセキュリティ問題が回避されますが、リーフとブランチが異なるためです。クライアントは、呼び出すときにリーフオブジェクトとブランチオブジェクトの存在を知る必要があるため、透過性が失われます。構造図を図2に示します。
パターンの実現
セットc0 = {leaf1、{leaf2、leaf3}}の要素にアクセスする場合、対応するツリー図を図3に示します。
- トランスペアレントコンビネーションモード
public class CompositePattern {
public static void main(String[] args) {
Component c0 = new Composite();
Component c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
//抽象构件
interface Component {
public void add(Component c);
public void remove(Component c);
public Component getChild(int i);
public void operation();
}
//树叶构件
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
public void add(Component c) {
}
public void remove(Component c) {
}
public Component getChild(int i) {
return null;
}
public void operation() {
System.out.println("树叶" + name + ":被访问!");
}
}
//树枝构件
class Composite implements Component {
private ArrayList<Component> children = new ArrayList<Component>();
public void add(Component c) {
children.add(c);
}
public void remove(Component c) {
children.remove(c);
}
public Component getChild(int i) {
return children.get(i);
}
public void operation() {
for (Object obj : children) {
((Component) obj).operation();
}
}
}
リーフ1:訪問してください!
リーフ2:訪問してください!
リーフ3:訪問してください!
- 安全な組み合わせモード
安全な組み合わせモードの実装コードは、透過的な組み合わせモードの実装コードと似ていますが、簡単な変更を加える限り、コードは次のようになります。
まず、コンポーネントコードを変更して、階層のパブリック動作のみを保持します。
interface Component {
public void operation();
}
次に、サブクラス操作を管理するためのメソッドを取得するために、クライアントコードを変更して、ブランチコンポーネントのタイプをCompositeタイプに変更します。
public class CompositePattern {
public static void main(String[] args) {
Composite c0 = new Composite();
Composite c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
4.組み合わせモードの適用例
【例1】コンビネーションモードは、店舗での買い物後にユーザーが選択した商品の情報を表示し、選択した商品の合計金額を計算する機能を実現するために使用されます。
注:李氏が少関の「天傑角」にある日用品店に行く場合、彼は赤い小さなバッグを使用して、呉源特産品を2パック(単価7.9元)、呉源の地図を1つ(単価9.9)梱包します。元);白を1つ使用する小さなバッグには、Shaoguan Fragrance(RMB 68)が2袋、Shaoguan黒茶(RMB 180)が3袋含まれています。中央のバッグには、赤い小さなバッグが前面にあり、Jingdezhen磁器(RMB 380)が1つ入っています。使用1大きなバッグには、フロントミドルバッグ、白い小さなバッグ、Li-Ningスポーツシューズ(単価198元)が含まれています。
最後の「ビッグバッグ」の内容は次のとおりです。{1ペアの李寧ブランドのスニーカー(単価198元)、白い小さなバッグ{Shaoguanキノコ2袋(単価68元)、Shaoguan黒茶3パケット(ユニット価格180元)}、中袋{1 Jingdezhen磁器(単価380元)、赤い小さな袋{Wuyuan料理の2つのパッケージ(単価7.9元)、1 Wuyuanマップ(単価9.9元)}}}、今それ李氏がすべての製品情報を大きな袋に入れていることを示し、支払われる合計価格を計算するためのプログラミングが必要です。
この例は、安全な組み合わせモードに従って設計でき、その構造図を図4に示します。
プログラムコードは次のとおりです。
package composite;
import java.util.ArrayList;
public class ShoppingTest {
public static void main(String[] args) {
float s = 0;
Bags BigBag, mediumBag, smallRedBag, smallWhiteBag;
Goods sp;
BigBag = new Bags("大袋子");
mediumBag = new Bags("中袋子");
smallRedBag = new Bags("红色小袋子");
smallWhiteBag = new Bags("白色小袋子");
sp = new Goods("婺源特产", 2, 7.9f);
smallRedBag.add(sp);
sp = new Goods("婺源地图", 1, 9.9f);
smallRedBag.add(sp);
sp = new Goods("韶关香菇", 2, 68);
smallWhiteBag.add(sp);
sp = new Goods("韶关红茶", 3, 180);
smallWhiteBag.add(sp);
sp = new Goods("景德镇瓷器", 1, 380);
mediumBag.add(sp);
mediumBag.add(smallRedBag);
sp = new Goods("李宁牌运动鞋", 1, 198);
BigBag.add(sp);
BigBag.add(smallWhiteBag);
BigBag.add(mediumBag);
System.out.println("您选购的商品有:");
BigBag.show();
s = BigBag.calculation();
System.out.println("要支付的总价是:" + s + "元");
}
}
//抽象构件:物品
interface Articles {
public float calculation(); //计算
public void show();
}
//树叶构件:商品
class Goods implements Articles {
private String name; //名字
private int quantity; //数量
private float unitPrice; //单价
public Goods(String name, int quantity, float unitPrice) {
this.name = name;
this.quantity = quantity;
this.unitPrice = unitPrice;
}
public float calculation() {
return quantity * unitPrice;
}
public void show() {
System.out.println(name + "(数量:" + quantity + ",单价:" + unitPrice + "元)");
}
}
//树枝构件:袋子
class Bags implements Articles {
private String name; //名字
private ArrayList<Articles> bags = new ArrayList<Articles>();
public Bags(String name) {
this.name = name;
}
public void add(Articles c) {
bags.add(c);
}
public void remove(Articles c) {
bags.remove(c);
}
public Articles getChild(int i) {
return bags.get(i);
}
public float calculation() {
float s = 0;
for (Object obj : bags) {
s += ((Articles) obj).calculation();
}
return s;
}
public void show() {
for (Object obj : bags) {
((Articles) obj).show();
}
}
}
購入する商品は次のとおりです。
李寧ブランドのスポーツシューズ(数量:1、単価:198.0元)
少関キノコ(数量:2、単価:68.0元)
少関黒茶(数量:3、単価:180.0元)
Jingdezhen磁器(数量:1。単価:380.0元)呉元
特産品(数量:2、単価:7.9元)呉元
地図(数量:1、単価:9.9元)
支払総額:1279.7元
5.複合モードのアプリケーションシナリオ
複合モードの構造と特性を上で分析し、それが適用可能な以下のアプリケーションシナリオを以下で分析します。
- オブジェクトの全体と一部の階層構造を表す必要がある場合
- 結合されたオブジェクトと単一のオブジェクトの違いをユーザーから隠す必要があり、ユーザーは統合されたインターフェイスで結合された構造内のすべてのオブジェクトを使用できます
6.コンビネーションモードの拡張
前に紹介した組み合わせモードのリーフノードとブランチノードが抽象化されている場合、つまり、リーフノードとブランチノードに子ノードがある場合、組み合わせモードはJavaAWTのような複雑な組み合わせモードに拡張されます。 / Swing単純なコンポーネントJTextComponentにはサブクラスJTextFieldとJTextAreaがあり、コンテナコンポーネントContainerにもサブクラスWindowとPanelがあります。複雑な組み合わせモードの構造図を図5に示します。