別の一般的な言語は、Java / Java EEと同様の複雑さを管理しながら、ファクトリパターンを使用する必要をどのように回避しますか?


23

ファクトリー・パターン(または少なくともの使用FactoryFactory..)は、次のような多くのジョークのお尻です

RequestProcessorFactoryFactory.RequestProcessorFactoryのような詳細で「創造的な」名前の他に、Java / C ++でプログラミングする必要があり、Abstract_factory_patternのユースケースがある場合、ファクトリパターンに根本的な問題はありますか?

他の一般的な言語(RubyScalaなど)を使用して、同様の複雑さを管理する必要はありませんか?

私が尋ねる理由は、Java / Java EEエコシステムのコンテキストで言及されている工場の批判がほとんど見られますが、他の言語/フレームワークがそれらをどのように解決するかについては説明しません。



4
問題は工場の使用ではなく、完全に細かいパターンです。エンタープライズJavaの世界で特に普及しているのは、工場の過剰使用です。
CodesInChaos 14

とてもいいジョークリンク。スパイスラックを構築するためのツールを取得する関数型言語の解釈を見てみたいです。それは本当に家に帰るだろうと思う。
パトリックM 14

回答:


28

あなたの質問には「Java」というタグが付けられています。Factoryパターンがwhy笑されている理由を尋ねているのは驚くことではありません。

たとえば、ファイルからXMLドキュメントを読み込んで、それに対してXPathクエリを実行してみてください。ファクトリーとビルダーをセットアップするには、10行のコードのようなものが必要です。

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder(); 

Document xmlDocument = builder.parse(new FileInputStream("c:\\employees.xml"));
XPath xPath =  XPathFactory.newInstance().newXPath();

xPath.compile(expression).evaluate(xmlDocument);

このAPIを設計している人たちが開発者として働いたことがあるのか​​、それとも本を読んで物を投げただけなのか、疑問に思います。パーサーを自分で書きたくなくて、タスクを他の人に任せただけだと理解していますが、それでもい実装になります。

ここでは、C#でXMLファイルをロードする代替手段を尋ねているため、

XDocument xml = XDocument.Load("c:\\employees.xml");
var nodes = xml.XPathSelectElements(expression);

新しくhatch化したJava開発者はFactoryの狂気を見て、それをしても大丈夫だと思います-Javaを自分で構築した天才がそれをそれほど使用しているなら。

Factoryおよびその他のパタ​​ーンは、それぞれ特定のジョブに適したツールです。それらをジョブに適用すると、theyいコードに縛られてしまいます。


1
C#の代替が新しいLINQ to XMLを使用していることに注意してください-古いDOMの代替は次のようにXmlDocument xml = new XmlDocument(); xml.Load("c:\\employees.xml"); XmlNodeList nodes = xml.SelectNodes(expression); なります。私は、MSが推奨する方法で、support.microsoft.com
ボブ14

36

よくあることですが、人々は何が起こっているのかを誤解しています(そしてそれには多くの笑いが含まれます)。
多くの人(おそらくほとんどの人)が使用するのと同じくらい悪いのは、工場のパターンそのものではありません。

そして、それは間違いなくプログラミング(およびパターン)の教え方に由来します。学童(多くの場合、「学生」と呼ばれます)は「パターンYを使用してXを作成する」ように言われます。
それで、彼らは学校で好きな特定のパターンを、それが適切かどうかにかかわらず、あらゆるものに対して適用し始めます。

そして、悲しいことに、ソフトウェア設計に関する本を書く大学教授も含まれます。
これの最高潮は、私がそのような人によって作成されたものを維持しなければならないという明確な不満を持っていたシステムでした(彼は彼の名前にオブジェクト指向のデザインに関するいくつかの本を持っていて、主要な大学のCS部門での指導職)。
これは3層パターンに基づいており、各層自体が3層システムです(分離する必要があります...)。両側の各層セット間のインターフェースには、他の層にデータを転送するオブジェクトを生成するファクトリーと、受信したオブジェクトを受信層に属するオブジェクトに変換するオブジェクトを生成するファクトリーがありました。
各ファクトリーには抽象ファクトリーがありました(そのファクトリーを変更する必要があるかもしれないので、呼び出しコードを変更する必要はありません...)。
そして、その混乱全体はもちろん完全に文書化されていませんでした。

システムには、第5正規形に正規化されたデータベースがありました(私はあなたが子供ではない)。

着信ドキュメントと発信ドキュメントを記録および追跡するために使用できるアドレス帳に本質的に近いシステムで、C ++で100 MBのコードベースと50を超えるデータベーステーブルがありました。ソフトウェアを実行しているコンピューターに直接接続されたプリンターを使用して、500の定型書簡を印刷するには72時間もかかります。

それが人々がパターンをpatterns笑する理由であり、特に人々は単一の特定のパターンにひたむきに焦点を合わせています。


40
他の理由は、ギャングオブフォーパターンの一部が、一流の機能を備えた言語で些細なことを行うことができるための非常に冗長なハックであり、それが避けられないアンチパターンであることです。「Strategy」、「Observer」、「Factory」、「Command」、および「Template Method」パターンは、関数を引数として渡すためのハックです。「Visitor」は、合計タイプ/バリアント/タグ付きユニオンでパターンマッチを実行するハックです。一部の人々が他の多くの言語では文字通り些細なことについて大騒ぎしているという事実は、人々をC ++、Java、および同様の言語のモックに導くものです。
ドーバル14

7
この逸話をありがとう。また、「代替案は何か」について少し詳しく説明していただけますか?質問の一部なので、私たちもそのようにはなりませんか?
フィリップ14

1
@Doval関数のみを渡すことができるときに、実行されたコマンドまたは呼び出されたストラテジーから値を返す方法を教えてください。そして、クロージャーと言う場合、それはクラスを作成することとまったく同じことを覚えておいてください。
陶酔14

2
@Euphoric私は問題を見ていません、詳しく説明してもらえますか?いずれにしても、それらはまったく同じものではありません。単一のフィールドを持つオブジェクトが変数へのポインター/参照とまったく同じである、または整数が(実際の)列挙型とまったく同じであると言うこともできます。実際には違いがあります。関数を比較しても意味がありません。匿名クラスの簡潔な構文はまだ見ていません。そして、同じ引数と戻り値の型を持つすべての関数は同じ型を持ちます(異なる名前を持つクラス/インターフェースは異なりますが、それらは同一であっても型は異なります。)
Doval 14

2
@Euphoric正しい。副作用なしで仕事を成し遂げる方法があるならば、それは悪い機能スタイルと考えられます。単純な代替方法は、合計タイプ/タグ付きユニオンを使用して、N種類の値のいずれかを返すことです。とにかく、実用的な方法はないと仮定しましょう。クラスとは異なります。これは実際にはインターフェイスです(OOPの意味で)。同じシグネチャを持つ関数のペアが交換可能であると考えるかどうかを確認するのは難しくありません。
ドーバル14

17

工場には、状況によってはエレガントなアプリケーション設計を可能にする多くの利点があります。1つは、後で作成するオブジェクトのプロパティを、ファクトリを作成して1か所で設定し、そのファクトリを渡すことができることです。しかし、多くの場合、実際にそれを行う必要はありません。その場合、Factoryを使用すると、実際に見返りを与えることなく、複雑さが追加されます。たとえば、このファクトリーを見てみましょう。

WidgetFactory redWidgetFactory = new ColoredWidgetFactory(COLOR_RED);
Widget widget = redWidgetFactory.create();

Factoryパターンに代わるものの1つは、非常によく似たBuilderパターンです。主な違いは、Factoryが初期化されると、Factoryによって作成されたオブジェクトのプロパティが設定され、Builderはデフォルト状態で初期化され、その後すべてのプロパティが設定されることです。

WidgetBuilder widgetBuilder = new WidgetBuilder();
widgetBuilder.setColor(COLOR_RED);
Widget widget = widgetBuilder.create();

しかし、オーバーエンジニアリングが問題である場合、FactoryをBuilderに置き換えても、それほど改善されない可能性があります。

いずれかのパターンの最も簡単な置換は、もちろん、new演算子を使用した単純なコンストラクターでオブジェクトインスタンスを作成することです。

Widget widget = new ColoredWidget(COLOR_RED);

ただし、コンストラクターには、ほとんどのオブジェクト指向言語で重大な欠点があります。つまり、コンストラクターはその正確なクラスのオブジェクトを返す必要があり、サブタイプを返すことはできません。

実行時にサブタイプを選択する必要があるが、そのためにまったく新しいBuilderクラスまたはFactoryクラスを作成したくない場合は、代わりにfactory-methodを使用できます。これは、そのクラスまたはそのサブクラスの1つの新しいインスタンスを返すクラスの静的メソッドです。内部状態を保持しないファクトリは、多くの場合、次のようなファクトリメソッドに置き換えることができます。

 Widget widget = Widget.createColoredWidget(COLOR_RED); // returns an object of class RedColoredWidget

Java 8の新機能は、ステートレスファクトリで行うのと同じように、メソッドを渡すことができるメソッド参照です。便利なことに、メソッド参照を受け入れるものはすべて、同じ機能的なインターフェースを実装するオブジェクトも受け入れます。これは、内部状態を備えた本格的なファクトリーでもあります。


2
多くの場合、ファクトリは作成後に構成することもできます。ビルダーとの主な違いは、afaikは通常、ビルダーが単一の複雑なインスタンスを作成するために使用されるのに対し、ファクトリーは多くの類似したインスタンスを作成するために使用されることです。
頭足類14

1
thanks(+1)、しかし答えは他のPLがそれをどのように解決するかを説明できません、それにもかかわらずJava 8メソッド参照を指摘してくれてありがとう。
senseiwu 14

1
オブジェクト指向のフレームワークでは、実際のオブジェクトの構築を問題の型に制限することは理にかなっており、foo = new Bar(23);と同等であるとよく考えていましたfoo = Bar._createInstance(23);。オブジェクトは、プライベートgetRealType()メソッドを使用して独自の型を確実に照会できる必要がありますが、オブジェクトは、への外部呼び出しによって返されるスーパータイプを指定できる必要がありますgetType()。外部コードは、への呼び出しがnew String("A")実際にString[たとえばのインスタンスに対してSingleCharacterString]のインスタンスを返すかどうかを気にするべきではありません。
supercat

1
コンテキストに基づいてサブクラスを選択する必要がある場合、多くの静的メソッドファクトリを使用しますが、その違いは呼び出し元にとって不透明でなければなりません。たとえば、すべてdraw(OutputStream out)がわずかに異なるhtmlを含むが生成する一連の画像クラスがあり、静的メソッドファクトリは状況に適したクラスを作成し、呼び出し元は単にdrawメソッドを使用できます。
マイケルショップシン14

1
@supercatでは、objective-cを見てみたいと思うかもしれません。オブジェクトの構築は、通常は単純なメソッド呼び出しで構成されます[[SomeClass alloc] init]。これは、(例えば、キャッシュされた値として)完全に異なるクラスのインスタンス又は別のオブジェクトを返すことが可能です
axelarge

3

ある種の工場は、適切な状況下ではほとんどすべてのオブジェクト指向言語に見られます。文字列などの単純なパラメータに基づいて、作成するオブジェクトの種類を選択する方法が必要な場合があります。

一部の人々はそれを取りすぎて、ファクトリー内を除いてコンストラクターを呼び出す必要がないようにコードを設計しようとします。工場を持っていると物事はばかげているようになります。

Scalaを学び、Rubyを知らないが、同じように信じているのは、言語が十分に表現力があり、プログラマが常に「配管」作業を外部構成ファイルにプッシュしようとしないことです。 。Scalaでは、異なるファクトリーファクトリーを作成してクラスをさまざまな構成に結び付けるのではなく、特性が混在したオブジェクトを使用します。また、Javaで頻繁に過剰に設計されるタスク用の言語で簡単なDSLを作成することも比較的簡単です。

また、他のコメントと回答が指摘しているように、クロージャーとファーストクラス関数は多くのパターンの必要性を取り除きます。そのため、C ++ 11とJava 8が広く採用されるにつれて、アンチパターンの多くが消え始めると思います。


ありがとう(+1)。あなたの答えは、Scalaがそれをどのように回避するかについての私の疑問のいくつかを説明しています。正直なところ、一度(もし)ScalaがエンタープライズレベルでJavaの少なくとも半分の人気を獲得し、フレームワークの軍隊、パターン、有料アプリサーバーなどが登場すると思いませんか?
senseiwu 14

もしScalaがJavaを追い越したなら、それは人々がソフトウェアを設計する「Scalaの方法」を好むからだと思います。JavaがJava 5のジェネリックやJava 8のラムダやストリームなど、Scalaへの切り替えを促進する機能を引き続き採用し続けていることは、おそらく最終的にプログラマーがアンチパターンを放棄する結果になることを期待している機能は利用できませんでした。どのように良い言語が得られても、FactoryFactoryの支持者は常に存在します。明らかに、これらのアーキテクチャを好む人もいれば、それほど一般的ではない人もいます。
カールビーレフェルト14

2

一般的に、Javaプログラムはひどく過剰に設計されているという傾向があります[要出典]。多くの工場を持つことは、オーバーエンジニアリングの最も一般的な症状の1つです。それが人々がそれらをからかう理由です。

特に、Javaの工場での問題は、Javaではa)コンストラクターが関数ではなく、b)関数がファーストクラスの市民ではないということです。

このようなものを書くことができると想像してください(JREのFunctionよく知られたインターフェースになりましょう)

// Framework code
public Node buildTree(Function<BranchNode, Node, Node> branchNodeFactory) {
    Node current = nextNode();
    while (hasNextNode()) {
        current = branchNodeFactory.call(current, nextNode());
    }
    return current
}

// MyDataNode.java
public class MyBranchNode implements BranchNode {

    public MyBranchNode(Node left, Node right) { ... }
}

// Client code
Node root = buildTree(MyBranchNode::new);

ファクトリインターフェースまたはクラスはありません。多くの動的言語には、一流の機能があります。残念ながら、これはJava 7以前では不可能です(同じことを工場で実装することは、読者への課題として残されています)。

そのため、代替案はそれほど「異なるパターンを使用する」のではなく、「より優れたオブジェクトモデルで言語を使用する」または「単純な問題を過度に設計しないでください」です。


2
これは、「選択肢は何ですか?」という質問に答えようとさえしません。
ブヨ

1
2番目の段落を過ぎて読むと、「他の言語がどのようにそれを行うか」の部分に到達します。(PS:他の答えも代替を示していません)
頭足類14

4
@gnat彼は間接的に代替案がファーストクラスの関数を使うことだと言っているのではないでしょうか?オブジェクトを作成できるブラックボックスが必要な場合、オプションはファクトリーまたは関数のいずれかであり、ファクトリーは変装した単なる関数です。
ドーバル14

3
「多くの動的言語で」は少し誤解を招く可能性があります。実際に必要なのは一流の関数を持つ言語だからです。「動的」はこれに直交しています。
アンドレスF. 14

本当ですが、動的言語でよく見られるプロパティです。回答の冒頭で、ファーストクラスの機能の必要性について言及しました。
頭足類14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.