簡介
如果創建某個對象需要大量重複代碼,應用層不關注這個對象的創建細節,創建對象的過程可以推遲到子類實現,那麼可以考慮使用工廠方法。工廠模式是通過簡單工廠演化而來,與簡單工廠相比,在加入新產品時符合開閉原則,擴展性更高。然而每增加一種產品,都需要創建對應的工廠類,會導致類數量過多,增加系統的複雜度。但這種基於產品和工廠的多態性設計,正是工廠方法的關鍵。
舉例
繼續我們在簡單工廠中教學視頻的例子。其實只需要將原來的工廠類拆分成兩層,抽象工廠和具體產品工廠。
抽象工廠中只需要定義創建產品的抽象方法,它可以是一個接口,也可以是抽象類。
public abstract class VideoFactory {
public abstract Video getInstance();
}
產品的創建就交由具體的工廠類實現,每種產品都有自己的工廠。
public class JavaVideoFactory extends VideoFactory {
public Video getInstance() {
return new JavaVideo();
}
}
public class PythonVideoFactory extends VideoFactory {
public Video getInstance() {
return new PythonVideo();
}
}
這裏的產品類和簡單工廠中一致,一個基類,一些實現基類的具體產品類。
public abstract class Video {
private String name;
private String describe;
public abstract void produce();
}
public class JavaVideo extends Video {
public void produce() {
System.out.println("This is Java class.");
}
}
public class PythonVideo extends Video {
public void produce() {
System.out.println("This is python class.");
}
}
應用層只需要創建對應的工廠對象,調用統一的獲取產品對象方法即可。
public class Test {
public static void main(String[] args) {
VideoFactory videoFactory = new JavaVideoFactory();
Video video = videoFactory.getInstance();
video.produce();
}
}
上文提過,工廠方法真正的實現了開閉原則。那我們就來需求擴展一下。加入現在又新增了一門MATLAB課程。(哈哈,原諒我對MATLAB的私心❤️
那麼我們只需要創建新的MATLAB工廠類和MATLAB視頻類分別繼承其基類即可,完全無需修改已有類。
public class MatlabVideoFactory extends VideoFactory {
public Video getInstance() {
return new MatlabVideo();
}
}
public class MatlabVideo extends Video {
public void produce() {
System.out.println("This is MATLAB class.");
}
}
當應用層需要時,只需創建MatlabVideoFactory對象,並從其中獲取即可。
public class Test {
public static void main(String[] args) {
VideoFactory videoFactory = new MatlabVideoFactory();
Video video = videoFactory.getInstance();
video.produce();
}
}
Logback中的工廠方法
Logback在創建Log時,使用的就是工廠方法。它的各個元素都是在同一個類sun.rmi.runtime的Log.java中用內部類實現,但這並不影響工廠方法的結構。它的抽象產品基類就是Log,其中一個產品實現類爲LoggerLog,抽象工廠類爲LogFactory,工廠實現類爲LoggerLogFactory。另一對是LogStreamLog和LogStreamLogFactory。其中關鍵代碼如下,爲了更加易懂,我調整了一下源碼順序。
public abstract class Log {
... ...
public abstract void log(Level var1, String var2);
private interface LogFactory {
Log createLog(String var1, String var2, Level var3);
}
private static class LoggerLog extends Log {
public void log(Level var1, String var2) {
if (this.isLoggable(var1)) {
String[] var3 = Log.getSource();
this.logger.logp(var1, var3[0], var3[1], Thread.currentThread().getName() + ": " + var2);
}
}
}
private static class LoggerLogFactory implements Log.LogFactory {
public Log createLog(String var1, String var2, Level var3) {
Logger var4 = Logger.getLogger(var1);
return new Log.LoggerLog(var4, var3);
}
}
private static class LogStreamLog extends Log {
public void log(Level var1, String var2) {
if (this.isLoggable(var1)) {
String[] var3 = Log.getSource();
this.stream.println(unqualifiedName(var3[0]) + "." + var3[1] + ": " + var2);
}
}
}
private static class LogStreamLogFactory implements Log.LogFactory {
public Log createLog(String var1, String var2, Level var3) {
LogStream var4 = null;
if (var2 != null) {
var4 = LogStream.log(var2);
}
return new Log.LogStreamLog(var4, var3);
}
}
}