
Factory Method を Java で
Factory Method は、 生成に関するデザインパターンの一つで、 具象クラスを指定することなく、 プロダクト (訳注: 本パターンでは、 生成されるモノのことを一般にプロダクトと呼びます) のオブジェクトを生成することを可能とします。
Factory Method では、 オブジェクトの生成において、 直接のコンストラクター呼び出し (new
演算子) 代わりに使用すべきメソッドを定義します。 サブクラスにおいてこのメソッドを上書きすることにより、 生成されるオブジェクトのクラスを変更します。
もし各種ファクトリー系のパターンやコンセプトの違いで迷った場合は、 ファクトリーの比較 をご覧ください。
複雑度:
人気度:
使用例: Factory Method パターンは、 Java コードでは広く使われます。 コードに高度の柔軟性を持たせたい時にとても役に立ちます。
このパターンは、 以下の Java コア・ライブラリーで使われています:
-
java.util.Calendar#getInstance()
-
java.util.ResourceBundle#getBundle()
-
java.text.NumberFormat#getInstance()
-
java.nio.charset.Charset#forName()
-
java.net.URLStreamHandlerFactory#createURLStreamHandler(String)
(プロトコルに応じて異なるシングルトン・オブジェクトを返却) -
java.util.EnumSet#of()
-
javax.xml.bind.JAXBContext#createMarshaller()
および他の類似メソッド。
見つけ方: 具象クラスで具象オブジェクトを作成し、 それを抽象型またはインターフェースのオブジェクトとして返すような生成メソッドの存在により、 Factory Method を識別できます。
クロス・プラットフォーム GUI 要素の作成
この例では、 Button はプロダクト、 ダイアログはクリエーターとして機能します。
ダイアログは、 種類によって、 それ独自の要素を必要とします。 ダイアログの種類に応じてサブクラスを作成し、 ファクトリー・メソッドを上書きするのはこのためです。
これで、 ダイアログの種類に応じて適切なボタンのクラスのインスタンスが生成されます。 基底ダイアログは、 共通のインターフェースを使ってプロダクトと連携するため、 多くの変更にもかかわらずコードは動作し続けます。
buttons
buttons/Button.java: 共通プロダクト・インターフェース
package refactoring_guru.factory_method.example.buttons;
/**
* Common interface for all buttons.
*/
public interface Button {
void render();
void onClick();
}
buttons/HtmlButton.java: 具象プロダクト
package refactoring_guru.factory_method.example.buttons;
/**
* HTML button implementation.
*/
public class HtmlButton implements Button {
public void render() {
System.out.println("<button>Test Button</button>");
onClick();
}
public void onClick() {
System.out.println("Click! Button says - 'Hello World!'");
}
}
buttons/WindowsButton.java: もう一つの具象プロダクト
package refactoring_guru.factory_method.example.buttons;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Windows button implementation.
*/
public class WindowsButton implements Button {
JPanel panel = new JPanel();
JFrame frame = new JFrame();
JButton button;
public void render() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("Hello World!");
label.setOpaque(true);
label.setBackground(new Color(235, 233, 126));
label.setFont(new Font("Dialog", Font.BOLD, 44));
label.setHorizontalAlignment(SwingConstants.CENTER);
panel.setLayout(new FlowLayout(FlowLayout.CENTER));
frame.getContentPane().add(panel);
panel.add(label);
onClick();
panel.add(button);
frame.setSize(320, 200);
frame.setVisible(true);
onClick();
}
public void onClick() {
button = new JButton("Exit");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.setVisible(false);
System.exit(0);
}
});
}
}
factory
factory/Dialog.java: 基底クリエーター
package refactoring_guru.factory_method.example.factory;
import refactoring_guru.factory_method.example.buttons.Button;
/**
* Base factory class. Note that "factory" is merely a role for the class. It
* should have some core business logic which needs different products to be
* created.
*/
public abstract class Dialog {
public void renderWindow() {
// ... other code ...
Button okButton = createButton();
okButton.render();
}
/**
* Subclasses will override this method in order to create specific button
* objects.
*/
public abstract Button createButton();
}
factory/HtmlDialog.java: 具象クリエーター
package refactoring_guru.factory_method.example.factory;
import refactoring_guru.factory_method.example.buttons.Button;
import refactoring_guru.factory_method.example.buttons.HtmlButton;
/**
* HTML Dialog will produce HTML buttons.
*/
public class HtmlDialog extends Dialog {
@Override
public Button createButton() {
return new HtmlButton();
}
}
factory/WindowsDialog.java: もう一つの具象クリエーター
package refactoring_guru.factory_method.example.factory;
import refactoring_guru.factory_method.example.buttons.Button;
import refactoring_guru.factory_method.example.buttons.WindowsButton;
/**
* Windows Dialog will produce Windows buttons.
*/
public class WindowsDialog extends Dialog {
@Override
public Button createButton() {
return new WindowsButton();
}
}
Demo.java: クライアント・コード
package refactoring_guru.factory_method.example;
import refactoring_guru.factory_method.example.factory.Dialog;
import refactoring_guru.factory_method.example.factory.HtmlDialog;
import refactoring_guru.factory_method.example.factory.WindowsDialog;
/**
* Demo class. Everything comes together here.
*/
public class Demo {
private static Dialog dialog;
public static void main(String[] args) {
configure();
runBusinessLogic();
}
/**
* The concrete factory is usually chosen depending on configuration or
* environment options.
*/
static void configure() {
if (System.getProperty("os.name").equals("Windows 10")) {
dialog = new WindowsDialog();
} else {
dialog = new HtmlDialog();
}
}
/**
* All of the client code should work with factories and products through
* abstract interfaces. This way it does not care which factory it works
* with and what kind of product it returns.
*/
static void runBusinessLogic() {
dialog.renderWindow();
}
}
OutputDemo.txt: 実行結果 (HtmlDialog)
<button>Test Button</button>
Click! Button says - 'Hello World!'
OutputDemo.png: 実行結果 (WindowsDialog)
