ファクトリメソッドパターン
オブジェクトを作成するためのインターフェイスを定義しますが、インスタンス化するクラスをサブクラスに決定させます。ファクトリメソッドパターンは、サブクラスへのクラスのインスタンス化を遅らせます。これはクラス作成モードです。
クラス図
成し遂げる
抽象ファクトリ
package design.factory;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public interface Factory {
public Product factoryMethod();
}
抽象製品
package design.factory;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public interface Product {
}
特定の製品
package design.factory;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public class ConcreteProduct implements Product {
}
特定の工場
package design.factory;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public class ConcreteFactory implements Factory {
public Product factoryMethod(){
return new ConcreteProduct();
}
}
クライアント
package design.factory;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public class Client {
public static void main(String[] args) {
Factory factory;
//可通过配置文件与反射机制实现
factory=new ConcreteFactory();
Product product;
product=factory.factoryMethod();
}
}
アプリケーション
ログロガー(ロガー)を実行しているシステムは、ファイル記録やデータベース記録など、さまざまな方法でシステムの実行ログを保存できます。ユーザーは、構成ファイルを変更することにより、ログ記録方法を柔軟に変更できます。さまざまなタイプのロガーを設計する際に、開発者はロガーに初期化作業が必要であることに気付きました。初期化パラメーターの設定プロセスはより複雑で、一部のパラメーターは厳密な順序で設定されます。そうしないと、記録に失敗する可能性があります。
レコーダの初期化プロセスをより適切にカプセル化し、複数のレコーダを切り替える柔軟性を確保するために、システムは現在、ファクトリメソッドパターンを使用して設計されています。注:writeLog()メソッドの実装は、単純な出力ステートメントのみを書き込みます。
コード
抽象製品
package design.factory.test;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public interface Logger {
public void writeLog();
}
特定の製品
package design.factory.test;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public class DatabaseLogger implements Logger {
public void writeLog() {
System.out.println("数据库日志记录");
}
}
package design.factory.test;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public class FileLogger implements Logger {
public void writeLog() {
System.out.println("文件日志记录");
}
}
抽象ファクトリ
package design.factory.test;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public interface LoggerFactory {
public Logger createLogger();
}
特定の工場
package design.factory.test;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public class DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
Logger logger = new DatabaseLogger();
return logger;
}
}
package design.factory.test;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() {
Logger logger = new FileLogger();
return logger;
}
}
クライアント
package design.factory.test;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public class Client {
public static void main(String[] args) {
LoggerFactory factory;
Logger logger;
factory = new FileLoggerFactory();
logger = factory.createLogger();
logger.writeLog();
}
}
問題があります。ロガー(特定のファクトリクラス)を置き換える必要がある場合は、クライアントコードを変更する必要があります。これは、開始と終了の原則を満たしていません。
解決策:構成ファイルを導入し、リフレクションメカニズムを使用して、クライアントコードを変更せずに特定のファクトリクラスを変更する
config.xml構成ファイルを追加します
<?xml version="1.0" encoding="UTF-8"?>
<config>
<className>design.factory.test.FileLoggerFactory</className>
</config>
ツールを追加する
package design.factory.test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class XMLUtil {
public static Object getBean() {
try {
// 创建DOM文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src//design//factory//test//config.xml"));
// 获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName("className");
Node classNode = nl.item(0).getFirstChild();
String cName = classNode.getNodeValue();
// 通过类名生成实例对象
Class c = Class.forName(cName);
Object obj = c.newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
クライアントコードは
package design.factory.test;
/*
*
*@author:zzf
*@time:2020-12-19
*
*/
public class Client {
public static void main(String[] args) {
LoggerFactory factory;
Logger logger;
//factory = new FileLoggerFactory();
factory = (LoggerFactory) XMLUtil.getBean();
logger = factory.createLogger();
logger.writeLog();
}
}
さらなる最適化
ログファミリーツリーの透過性を実現するためにファクトリ基本クラスを変更してみてください。つまり、クライアントはログファミリーツリーの情報をまったく知りません(ヒント:ファクトリメソッドを使用して非表示にします)
ファクトリメソッドの非表示
クライアントの使用をさらに簡素化するために、ファクトリメソッドをクライアントから非表示にすることもできます。この場合、製品クラスのビジネスメソッドはファクトリクラスで直接呼び出されます。クライアントは呼び出す必要はありません。製品オブジェクトを作成するためのファクトリメソッド。ファクトリオブジェクトを使用して、作成された製品オブジェクト内のすべてのビジネスメソッドを呼び出すことができます。
コード
抽象ファクトリインターフェイスを抽象クラスに変更します
public abstract class LoggerFactory {
public void writeLog() {
Logger logger = this.createLogger();
logger.writeLog();
}
public abstract Logger createLogger();
}
クライアントコードの変更
public class Client {
public static void main(String[] args) {
LoggerFactory factory;
factory = (LoggerFactory) XMLUtil.getBean();
factory.writeLog();
}
}
ファクトリメソッドパターンの長所と短所
利点:
(1)顧客が必要とする製品を作成するための特別なファクトリメソッドを提供すると同時に、特定の製品カテゴリが顧客からインスタンス化される詳細を非表示にします
(2)ファクトリロールと製品ロールのポリモーフィズムに基づくデザインはファクトリメソッドパターンの鍵です。これにより、ファクトリは作成する製品オブジェクトを独自に決定でき、このオブジェクトの作成方法の詳細は特定のファクトリに完全にカプセル化されます。
(3)新しい製品がシステムに追加されると、開閉の原則は次のようになります。完全に準拠
短所:
(1)システム内のクラスの数がペアで増加し、システムの複雑さがある程度
増加し、システムに追加のオーバーヘッドが発生します(2)抽象化と理解の難しさが増加しますシステム
該当する環境
(1)クライアントは必要なオブジェクトのクラスを知りません
(2)抽象ファクトリクラスは、そのサブクラスを介して作成するオブジェクトを指定します
参照
Javaデザインパターン