2

Head First Design Pattern を使用して Object Oriented Design パターンを学習しようとしています。これは、本からのファクトリーパターンの一例です。ここでは、オープンクローズの原則に違反することなく、新しいピザアイテムを追加したいと考えています。本のサンプル コードでは、新しいピザ アイテム クラスを追加する場合、PizzaStore クラスと PizzaOrder クラスを変更する必要があります。しかし、他のクラスを変更せずに、新しいピザ アイテムを追加したいだけです。

public class ChicagoPizzaStore extends PizzaStore {

Pizza createPizza(String item) {
        if (item.equals("cheese")) {
                return new ChicagoStyleCheesePizza();
        } else if (item.equals("veggie")) {
                return new ChicagoStyleVeggiePizza();
        } else if (item.equals("clam")) {
                return new ChicagoStyleClamPizza();
        } 
            else return null;
}

}

この PizzaStore クラスは、ピザを作成して注文するためのものです。

public abstract class PizzaStore {

    abstract Pizza createPizza(String item);

public Pizza orderPizza(String type) {
    Pizza pizza = createPizza(type);
    System.out.println("--- Making a " + pizza.getName() + " ---");
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza;
}

}

これは、ピザの抽象クラスです。

public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();

void prepare() {
    System.out.println("Preparing " + name);
    System.out.println("Tossing dough...");
    System.out.println("Adding sauce...");
    System.out.println("Adding toppings: ");
    for (int i = 0; i < toppings.size(); i++) {
        System.out.println("   " + toppings.get(i));
    }
}

このクラスは、顧客から注文を受けるために使用されます。

 public class PizzaTestDrive {

        public static void main(String[] args) {
            PizzaStore nyStore = new NYPizzaStore();
            PizzaStore chicagoStore = new ChicagoPizzaStore();

            Pizza pizza = nyStore.orderPizza("cheese");
            System.out.println("Ethan ordered a " + pizza.getName() + "\n");
    }
}

これは私の新しいピザ アイテム クラスです。chicagoPizzaStore と testDrive クラスを変更せずに、このピザを注文したいと思います。

public class ChicagoStyleClamPizza extends Pizza {
    public ChicagoStyleClamPizza() {
        name = "Chicago Style Clam Pizza";
        dough = "Extra Thick Crust Dough";
        sauce = "Plum Tomato Sauce";
        toppings.add("Shredded Mozzarella Cheese");
        toppings.add("Frozen Clams from Chesapeake Bay");
    }

    void cut() {
        System.out.println("Cutting the pizza into square slices");
    }
}
4

6 に答える 6

9

現状ではChicagoPizzaStore、新しいタイプのピザ ( の新しいサブクラスPizza) が登場するたびに、具象クリエーター メソッドに機能を追加してcreatePizza(String item)、ピザ ストアがそれらのタイプのピザを作成できるようにする必要があります。

ご指摘のとおり、これはOCPに違反しています。

以下に、この問題に対する 2 つの解決策を示します。

1. 内部でリフレクションを使用しcreatePizza(String item)て動的にピザを作成する

このソリューションでは、OCP の原則に最後にもう一度違反する必要がありますが、リフレクションを使用してPizzaインスタンスを動的に作成するということChicagoPizzaStoreは、この変更を超えて、Pizza の将来のフレーバーをサポートするために変更する必要がなくなることを意味します。

クラスの新しいタイプのPizza名前は、create Pizza メソッドに提供されるキー (item 引数) の名前と一致する必要があります。ソリューションは次のように機能します。

public class ChicagoPizzaStore extends PizzaStore {

Pizza createPizza(String item) {
        try {
            //some assumptions about the classpath locations made here
            return Class.forName(item).newInstance();
        } catch(Exception e) {
            return null;
        }
}

新しいタイプのが作成されると、これらはメソッドにPizzaキーとして渡されるだけで作成されます。createPizza(item)

同様に、 のタイプがPizzaメニューから削除された場合、そのようなピザのクラス定義をクラスパスから削除するとcreatePizza(item)、ディスカウントされたフレーバーに対して null が返されます。

リフレクションの使用にはさまざまな理由で批判がありますが、リフレクションの批判はこの質問の範囲外であり、オープン/クローズドに準拠するファクトリの実装の問題に対する完全に有効な解決策ではありません。原理。

2. サブクラス ChicagoPizzaStore

SOLID の O が示すように、クラスは「拡張に対してオープンで、変更に対してクローズ」です。したがって、問題の解決策は単に拡張することChicagoPizzaStoreです:

public class ExtendedChicagoPizzaStore extends ChicagoPizzaStore {

    Pizza createPizza(String item) {
            if (item.equals("spicy")) {
                    return new RidiculouslySpicyPizza();
            } else {
                    return super.createPizza(item);
           }
    }

このソリューションには、OCP を適用するために違反しないという利点があります。

于 2014-12-18T15:03:42.237 に答える
0

ここに実行例があります

class FactoryClosedForModification {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            ShapeFactory sf = new ShapeFactory();
            Shape shape = (Shape) sf.getShape(Triangle.class.getName());
            shape.draw();
        }
    }


class ShapeFactory {

    public Object getShape(String shapeName)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return Class.forName(shapeName).newInstance();
    }
}

class Shape {
    public void draw() {
        System.out.println("Drawing a shape.");
    }
}

class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Triangle.");
    }
}

class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Circle.");
    }
}
于 2020-06-28T10:59:44.640 に答える
-1

リフレクションを使用して開閉原理を満たすと、パフォーマンスが低下します。むしろ、他の単純な手法を使用して、開閉原理に従って工場を作成できます。Factory Design Patterns と Open-Closed Principle (OCP)、SOLID の「O」は 、これについてより適切な説明を提供します。この記事では、単純なファクトリを調整してオープン クローズドの原則に従う方法についても説明します。

于 2013-06-07T05:30:46.517 に答える
-1

ファクトリ メソッドのデザイン パターンのポイントは、クライアントと具体的な製品を疎結合にすることです。クライアントは、インターフェイスまたは基本クラスのみと対話します。したがって、将来、新しい具体的な製品クラス (あなたの場合はピザ) を追加する必要がある場合、新しいクラスはクライアント コード (あなたの場合PizzaTestDrive) に変更をもたらすべきではありません。新しい製品 (Pizza) を追加するには、Concrete Factory クラス (あなたの場合ChicagoPizzaStore) を変更するだけで済みます。

Factory Method 設計パターンの実装は正しいと思います。新しい Pizza を追加する場合、クライアント コードは変更されず、Concrete Factory クラスのみが変更されます。

于 2012-10-14T13:23:22.317 に答える