web-dev-qa-db-ja.com

ファクトリの作成と継承の違い

「Head First Design Patterns」から「Factory method」のデザインパターンを読んでいます。だから、クラスがあります

_public class PizzaStore {

    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {

        Pizza pizza;

        pizza = factory.createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }
}
_

上記のように、ファクトリを格納してからcreatePizza(String)仮想関数を呼び出します。これにより、具体的なファクトリクラスに適切なcreatePizza(...)関数が呼び出されます(例:NYStylePizzaFactoryまたはChicagoStylePizzaFactory)。しかし、その後、著者は続けます:

しかし、もう少し品質管理が必要です...!

コードで次の変更を行います。

_public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = createPizza();
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    abstract Pizza createPizza();
}

public class NYStylePizzaStore : PizzaStore {
    Pizza createPizza() {
        return new NYStylePizza();
    }
}

public class ChicagoStylePizzaStore : PizzaStore {
    Pizza createPizza() {
        return new ChicagoStylePizza();
    }
}
_

では、後者の場合、なぜ品質管理が強化されるのかわかりません。結果は同じようです-適切な仮想関数が呼び出されます。

3
David Hovsepyan

私は本を​​持っていませんが、このトピックについていくつか 素晴らしいスライド を見つけました。スライドは本に従っていると想定します。彼らはこれがピザの作成を制御することについてであるという私の最初の推測を確認するようです。

最初の例では、ストアはピザの作成をファクトリーに委任し、ファクトリーは作成コードによって外部から注入されます。店自体はピザがどのように作られるかについては何も言いません、それはただそれらを工場に頼むだけです。

2番目の例では、ストアはcreatePizzaを介してピザの作成を制御します。

ここで、「コンクリート工場が店ではなくピザを作成する方が論理的ではないか」と質問します。

2番目の例にもファクトリがあり、それだけがオブジェクトではなくメソッドであることに注意してください。質問を書き直してみましょう:

ファクトリメソッドの代わりにインジェクトされたファクトリを使用する方が論理的ではありませんか?

多分そうです。私のスライドは、フランチャイザーとしてのあなたが、注文準備の管理を維持しながら、ピザの作成を店舗に管理させたいことを示唆しています。これは理にかなっています。

しかし、そこから「あなたが本当にやりたいことは、店とピザの作成を結び付けるフレームワークを作成すること」にジャンプし、その理由がわかりません。

orderPizzaを実装する抽象基本クラスとcreatePizzaを実装するサブクラスは、上記のモデルよりもわかりやすいので、これにより、作成者はファクトリパターンを紹介する良い言い訳になります。しかし、地図は領域ではなく、ソフトウェア設計者はフランチャイザーやピザ店のフランチャイズ店とは異なる懸念を抱いています。より単純なモデルが最良のモデルであり、現実の世界での制御がOOソフトウェアでの制御と同じではないということは、必ずしも真実ではありません。

1

実行時(例#1)とコンパイル時(例#2)で何かの正しさをチェックすることの違いです。

最初の例では、ピザの種類を判別するために文字列を渡す必要があります。これにより、ランタイムエラーが発生する可能性があります。

PizzaStore store = new PizzaStore();
Pizza pizza1 = store.orderPizza("chicago style"); // Seems right
Pizza pizza2 = store.orderPizza("blue jeans");    // Blatantly wrong, but compiles

2番目のピザタイプはおそらくピザ工場(「ブルージーンズ」ピザについて聞いたことがありますか?)には理解されないでしょう。その時点で、ピザ工場は例外をスローします。

2番目の例では、ピザストアとピザスタイルの両方にクラスと継承を使用しているため、コンパイル時にピザタイプが正しいことを確認します。

7
Greg Burghardt

まず、本のP109から完全な段落を引用させてください。

"しかし、もう少し品質管理が必要です...!

つまり、SimpleFactoryのアイデアをテストして市場に出しました。フランチャイズがあなたの工場を使用してピザを作成していることがわかりましたが、残りのプロセスでは独自の自家製の手順を採用し始めました。ピザを切るのを忘れて、サードパーティの箱を使うでしょう。」

以下は、この段落に対する私の理解です。つまり、この場合にSimple Factoryパターンを使用する場合、 '品質管理 "が問題になる可能性があると著者が主張したのはなぜですか。

あるフランチャイズの所有者がフランチャイズ料をもう支払わないことを決定し、それをノックオフストア、つまりピザハットに変えたいと想像してみてください。 -)ピザハットに。

以前、彼は(ニューヨークにいるので)やっています:

NYStylePizzaStore nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
Pizza pizza = nyStore.order("Veggie") // This is a good veggie pizza to eat!

現在、彼は次のことを行うことにしました。

NYStylePizzaStore nyFactory = new NYPizzaFactory();
Pizza pizza = nyfactory.createPizza("Veggie")
pizza.box() // why are we boxing a raw pizza?
pizza.cut() // and now we're cutting the entire box?
pizza.bake() // and bake it?

彼は正しい順序で物事を行っていないことに注意してください!そして、顧客は焦げたピザが入った油っぽい箱を手に入れることになります。さらに悪いことに、顧客は彼らが合法的なピザハットからピザを手に入れていると思っていました(彼は店名が密かにピザハット!)。これは、フランチャイズビジネスの評判を完全に台無しにするでしょう。

ここでのポイントは、ファクトリー・メソッド・パターンを使用して、一般に公開するのではなく、ファクトリー・ロジックを適切にカプセル化する必要があるということです。だれも自分の「改善」をPizzaStoreプロシージャに簡単に追加することができないので、「品質管理」の問題を改善するのに役立ちます(そもそも、未焼成の生のピザにアクセスすることはできません。)

言い換えれば、あなたがフランチャイズ料を支払い、合法的なフランチャイズ所有者である場合にのみ、ピザ工場を使用する必要があります。ピザの作成手順とピザの作成手順は、実際に結び付ける必要があります。

1
yuanlu0210