Composite Pattern
問題の状況
Pancake house menu、diner menuとそのサブメニューデザートmenu、cafe menuをプリントアウトしたいです!この場合、各menuのタイプは、タイル、List、HashMapとは異なります.
この場合、iterator patternを使用して異なるタイプのメニューの出力を抽象化しますが、サブメニューをより柔軟に処理したい場合はどうすればいいですか?
つまり、整理要求は以下の通りです.
ツリー構造、各メニューとそのサブメニューを収容可能構造 、すべてのメニューに適用
より柔軟な構成、メニューを選択的に処理
この場合、コンビネーションモードを使用できます.
定義#テイギ#
オブジェクトをツリー構造に整理して、部分と全体を表す階層にすることができます.
クライアントでは、単一のオブジェクトと他のオブジェクトからなる複合オブジェクトを同じ方法で処理できます.
上記の例では、diner menuのスイーツmenuのように、オブジェクト内の形状を単一のオブジェクトと同じ形状と見なすことができます.個々のオブジェクトを他のオブジェクトを含まない複合オブジェクトと見なします.
コンポーネント:複合オブジェクト内のすべてのオブジェクトにinter彭を定義する
Leaf:Componentがサポートする機能の実装
Component:サブコンポーネントの動作を定義し、サブコンポーネントを保存します.Leaf関連機能の実装
Componentにはコンポーネントが入っています.
コンポーネントは、Composite、Leafの2つに分類されます.
->再帰構造
ルートはComponent最後に逆ツリー構造,すなわちLeafである.
MenuComponentでMenuItemとMenuで使用するメソッドを定義する
MenuItemとMenuは、自分が使っている方法だけを上書きすることで再定義します
メニューコンポーネント
複合モードは、上記の例に示すように、単一のロールの原則に違反し、コンポーネント内で階層を管理し、メニューに関連する操作を処理します.しかしながら、クライアントは、その要素がcompositeであるかleaf nodeであるかを透明に知ることができるため、透明性を確保するモードと考えられる.
leafノードでコンビネーションに関連するメソッドを呼び出したり再定義したり、instanceofを使用して再検証する必要があるなどの欠点がありますが、leafノードをサブオブジェクトのないコンビネーションオブジェクトと見なす場合は問題ありません.
状況によって決まる
Iterator Pattern
まだ未解決の要求条件があります.すなわち、所望のメニューは、ツリー構造全体のメニューにおいて選択的に操作処理することができる.そのため、Ipharter Patternを使用します.
Menu
クライアントは、リーフノードまたは複合オブジェクトを考慮する必要がなく、最上位ノードを使用してツリー全体で作業できます.
->部分-オブジェクト関係のあるすべてのオブジェクトセットを同じ方法で処理する場合に使用しましょう.
Pancake house menu、diner menuとそのサブメニューデザートmenu、cafe menuをプリントアウトしたいです!この場合、各menuのタイプは、タイル、List、HashMapとは異なります.
この場合、iterator patternを使用して異なるタイプのメニューの出力を抽象化しますが、サブメニューをより柔軟に処理したい場合はどうすればいいですか?
つまり、整理要求は以下の通りです.
ツリー構造、各メニューとそのサブメニューを収容可能
より柔軟な構成、
この場合、コンビネーションモードを使用できます.
定義#テイギ#
オブジェクトをツリー構造に整理して、部分と全体を表す階層にすることができます.
クライアントでは、単一のオブジェクトと他のオブジェクトからなる複合オブジェクトを同じ方法で処理できます.
上記の例では、diner menuのスイーツmenuのように、オブジェクト内の形状を単一のオブジェクトと同じ形状と見なすことができます.個々のオブジェクトを他のオブジェクトを含まない複合オブジェクトと見なします.
コンポーネント:複合オブジェクト内のすべてのオブジェクトにinter彭を定義する
Leaf:Componentがサポートする機能の実装
Component:サブコンポーネントの動作を定義し、サブコンポーネントを保存します.Leaf関連機能の実装
Componentにはコンポーネントが入っています.
コンポーネントは、Composite、Leafの2つに分類されます.
->再帰構造
ルートはComponent最後に逆ツリー構造,すなわちLeafである.
MenuComponentでMenuItemとMenuで使用するメソッドを定義する
MenuItemとMenuは、自分が使っている方法だけを上書きすることで再定義します
メニューコンポーネント
public abstract class MenuComponent {
// 메소드들은 하위 클래스에서 구현하지 않을 경우 기본적으로 오류 던지도록 구현
// MenuComponent 관련 메소드
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
// MenuItem 관련 메소드
public String getName() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
}
メニュー項目(Leaf)public class MenuItem extends MenuComponent {
String name;
String description;
boolean vegetarian;
double price;
public MenuItem(String name,
String description,
boolean vegetarian,
double price)
{
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
// 필요한 메소드 재정의
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
メニュー(Component)public class Menu extends MenuComponent {
ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
// 필요한 메소드 재정의
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("---------------------");
// 이터레이터 패턴 사용하여 반복
Iterator<MenuComponent> iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent =
(MenuComponent)iterator.next();
menuComponent.print();
}
}
}
サービス(クライアント)public class Waitress {
MenuComponent allMenus;
// 다른 모든 메뉴를 포함하고 있는 최상위 메뉴 구성요소 넘겨주기
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
// 최상위 메뉴 구성요소 출력하면 트리에 속한 모든 메뉴 출력 가능
public void printMenu() {
allMenus.print();
}
}
テストpublic class MenuTestDrive {
public static void main(String args[]) {
MenuComponent pancakeHouseMenu =
new Menu("PANCAKE HOUSE MENU", "Breakfast");
MenuComponent dinerMenu =
new Menu("DINER MENU", "Lunch");
MenuComponent cafeMenu =
new Menu("CAFE MENU", "Dinner");
MenuComponent dessertMenu =
new Menu("DESSERT MENU", "Dessert of course!");
MenuComponent coffeeMenu = new Menu("COFFEE MENU", "Stuff to go with your afternoon coffee");
MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
allMenus.add(pancakeHouseMenu);
allMenus.add(dinerMenu);
allMenus.add(cafeMenu);
// pancakeHouseMenu
pancakeHouseMenu.add(new MenuItem(
"K&B's Pancake Breakfast",
"Pancakes with scrambled eggs and toast",
true,
2.99));
pancakeHouseMenu.add(new MenuItem(
"Regular Pancake Breakfast",
"Pancakes with fried eggs, sausage",
false,
2.99));
pancakeHouseMenu.add(new MenuItem(
"Blueberry Pancakes",
"Pancakes made with fresh blueberries, and blueberry syrup",
true,
3.49));
pancakeHouseMenu.add(new MenuItem(
"Waffles",
"Waffles with your choice of blueberries or strawberries",
true,
3.59));
// dinerMenu
dinerMenu.add(new MenuItem(
"Vegetarian BLT",
"(Fakin') Bacon with lettuce & tomato on whole wheat",
true,
2.99));
dinerMenu.add(new MenuItem(
"BLT",
"Bacon with lettuce & tomato on whole wheat",
false,
2.99));
dinerMenu.add(new MenuItem(
"Soup of the day",
"A bowl of the soup of the day, with a side of potato salad",
false,
3.29));
dinerMenu.add(new MenuItem(
"Hot Dog",
"A hot dog, with saurkraut, relish, onions, topped with cheese",
false,
3.05));
dinerMenu.add(new MenuItem(
"Steamed Veggies and Brown Rice",
"Steamed vegetables over brown rice",
true,
3.99));
dinerMenu.add(new MenuItem(
"Pasta",
"Spaghetti with marinara sauce, and a slice of sourdough bread",
true,
3.89));
// dinerMenu에 dessertMenu 추가
dinerMenu.add(dessertMenu);
// dessertMenu
dessertMenu.add(new MenuItem(
"Apple Pie",
"Apple pie with a flakey crust, topped with vanilla icecream",
true,
1.59));
dessertMenu.add(new MenuItem(
"Cheesecake",
"Creamy New York cheesecake, with a chocolate graham crust",
true,
1.99));
dessertMenu.add(new MenuItem(
"Sorbet",
"A scoop of raspberry and a scoop of lime",
true,
1.89));
// cafeMenu
cafeMenu.add(new MenuItem(
"Veggie Burger and Air Fries",
"Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
true,
3.99));
cafeMenu.add(new MenuItem(
"Soup of the day",
"A cup of the soup of the day, with a side salad",
false,
3.69));
cafeMenu.add(new MenuItem(
"Burrito",
"A large burrito, with whole pinto beans, salsa, guacamole",
true,
4.29));
// cafeMenu에 coffeeMenu 추가
cafeMenu.add(coffeeMenu);
// coffeeMenu
coffeeMenu.add(new MenuItem(
"Coffee Cake",
"Crumbly cake topped with cinnamon and walnuts",
true,
1.59));
coffeeMenu.add(new MenuItem(
"Bagel",
"Flavors include sesame, poppyseed, cinnamon raisin, pumpkin",
false,
0.69));
coffeeMenu.add(new MenuItem(
"Biscotti",
"Three almond or hazelnut biscotti cookies",
true,
0.89));
Waitress waitress = new Waitress(allMenus);
waitress.printMenu(); // print
}
}
単一キャラクタの原則(SRP)と透明性複合モードは、上記の例に示すように、単一のロールの原則に違反し、コンポーネント内で階層を管理し、メニューに関連する操作を処理します.しかしながら、クライアントは、その要素がcompositeであるかleaf nodeであるかを透明に知ることができるため、透明性を確保するモードと考えられる.
leafノードでコンビネーションに関連するメソッドを呼び出したり再定義したり、instanceofを使用して再検証する必要があるなどの欠点がありますが、leafノードをサブオブジェクトのないコンビネーションオブジェクトと見なす場合は問題ありません.
状況によって決まる
Iterator Pattern
まだ未解決の要求条件があります.すなわち、所望のメニューは、ツリー構造全体のメニューにおいて選択的に操作処理することができる.そのため、Ipharter Patternを使用します.
Menu
public class Menu extends MenuComponent {
Iterator<MenuComponent> iterator = null;
.
.
.
// Menu에 CompositeIterator 추가
// 현재 복합 객체에 대한 반복자 리턴
public Iterator<MenuComponent> createIterator() {
if (iterator == null) {
iterator = new CompositeIterator(menuComponents.iterator());
}
return iterator;
}
.
.
.
MenuItempublic class MenuItem extends MenuComponent {
.
.
.
// NullItertor 추가
// Leaf는 반복자가 존재하지 않으므로 아무 일도 하지 않는 null 반복자 리턴
public Iterator<MenuComponent> createIterator() {
return new NullIterator();
}
.
.
.
CompositeIteratorpublic class CompositeIterator implements Iterator<MenuComponent> {
// 위의 코드에서는 leaf node 안에서 반복자를 써서 작업을 처리하고 composite인 경우 하위 메서드를 호출하여 반복
// 하지만 해당 코드에서는 iterator를 사용하므로 외부에서 반복자 사용 -> 스택 사용해야 함
Stack<Iterator<MenuComponent>> stack = new Stack<Iterator<MenuComponent>>();
// 최상위 composite의 iterator 전달
public CompositeIterator(Iterator<MenuComponent> iterator) {
stack.push(iterator);
}
public MenuComponent next() {
if (hasNext()) {
Iterator<MenuComponent> iterator = stack.peek();
MenuComponent component = iterator.next();
stack.push(component.createIterator());
return component;
} else {
return null;
}
}
public boolean hasNext() {
if (stack.empty()) {
return false;
} else {
Iterator<MenuComponent> iterator = stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
} else {
return true;
}
}
}
/*
* No longer needed as of Java 8
*
* (non-Javadoc)
* @see java.util.Iterator#remove()
*
public void remove() {
throw new UnsupportedOperationException();
}
*/
}
Waitresspublic class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu() {
allMenus.print();
}
public void printVegetarianMenu() {
Iterator<MenuComponent> iterator = allMenus.createIterator();
System.out.println("\nVEGETARIAN MENU\n----");
while (iterator.hasNext()) {
// iterator 이용해서 채식주의자 메뉴만 출력
MenuComponent menuComponent = iterator.next();
try {
if (menuComponent.isVegetarian()) {
menuComponent.print();
}
} catch (UnsupportedOperationException e) {}
// 여기서 try-catch를 사용한 이유
// client에서 composite, leaf를 똑같이 취급하고 composite에서 지원하지 않는 메소드임을 나타내기 위해서 사용
}
}
}
シングルラインサマリークライアントは、リーフノードまたは複合オブジェクトを考慮する必要がなく、最上位ノードを使用してツリー全体で作業できます.
->部分-オブジェクト関係のあるすべてのオブジェクトセットを同じ方法で処理する場合に使用しましょう.
Reference
この問題について(Composite Pattern), 我々は、より多くの情報をここで見つけました https://velog.io/@s2moon98/Composite-Patternテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol