設計模式二–工廠模式
舉例:
女媧採集黃土捏成人的形狀,然後放到八卦爐中燒製,然後放到土地上生長,但是意外隨時都會發生。
- 第一次烤人,感覺應該熟了,往地上一放,哇,沒烤熟,白人誕生了。
- 第二次烤人,上一次沒有烤熟,這次多烤一會,放到世間一看,哇,熟過頭了,於是黑人誕生了。
- 第三次烤人,一邊燒製一遍看,知道表皮發黃,嘿,剛剛好,於是黃種人誕生了。
思考:
- 在面向對象編程的思想中,萬物皆對象。於是我們抽象出具體的三個對象。
- 女媧,八卦爐,三種不同膚色的人。
- 女媧可以用場景類Client來表示,八卦爐可以用工廠來表示,三個不同膚色的人,他們都是同一個接口下面的不同實現類。
於是我們生成類圖。
- AbstarctHummanFactory是一個抽象類,HumanFactoruy是一個實現類,完成具體的任務,創建人類。Human是人類- 總稱,三個人種是其實現類,Nawa是一個場景,負責模擬這個場景,發起任務。
人類接口
/**
* @atuhor sha1024
*/
public interface Human {
void getTalk();
void getColor();
}
白人人類
/**
* @atuhor sha1024
*/
public class WhiteHuman implements Human{
@Override
public void getTalk() {
System.out.println("我是一個白人");
}
@Override
public void getColor() {
System.out.println("我的皮膚是白色的");
}
}
黃色人種
/**
* @atuhor sha1024
*/
public class YellowHuman implements Human{
@Override
public void getTalk() {
System.out.println("黃種人說話最好聽");
}
@Override
public void getColor() {
System.out.println("我的皮膚是黃色的");
}
}
黑色人種
/**
* @atuhor sha1024
*/
public class BlackHuman implements Human{
@Override
public void getTalk() {
System.out.println("黑人說話聽不懂");
}
@Override
public void getColor() {
System.out.println("我的皮膚是黑色的");
}
}
定義一個抽象工廠,定義了一個泛型實現了Human
/**
* @atuhor sha1024
*/
public abstract class AbstarctHummanFactory {
public abstract <T extends Human> T createHuman(Class<T> c);
}
定義了一個人類工廠實現抽象人類工廠
public class HummanFactory extends AbstarctHummanFactory {
@Override
public <T extends Human> T createHuman(Class<T> c) {
//定義一個人種
Human human = null;
try{
human = (T)Class.forName(c.getName()).newInstance();
}catch (Exception e){
throw new RuntimeException("定義人種錯誤");
}
return (T)human;
}
}
女媧類,實現具體的場景
public static void main(String[] args) {
AbstarctHummanFactory hummanFactory = new HummanFactory();
Human whiteHuman = hummanFactory.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.getTalk();
Human blackHuman = hummanFactory.createHuman(BlackHuman.class);
blackHuman.getTalk();
blackHuman.getColor();
Human yellowHuman = hummanFactory.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.getTalk();
SpringApplication.run(FactoryApplication.class, args);
}
執行結果
工廠方法模式的定義
- 定義一個用於創建對象的接口,讓子類來決定實例化哪一個。工廠方法使一個類的實例化延遲到其子類。
在工廠模式中,抽象產品類Product負責定義產品的共性,實現對事物最抽象的定義;Creator爲抽象創建類,也就是抽象工廠,具體如何創建產品類是由具體的實現工廠ConcreateCreator完成的。
具體的產品類可以有多個,都繼承抽象產品類
/**
* 抽象產品類
* @atuhor sha1024
*/
public abstract class Product {
//產品類的公共方法
public void method1(){
System.out.println("我是抽象工廠方法1");
}
/**
* 抽象方法
*/
public abstract void method2();
}
產品類1
/**
* @atuhor sha1024
*/
public class ConcreateProduct1 extends Product {
@Override
public void method2() {
/**
* 具體業務邏輯
*/
}
}
產品類2
/**
* @atuhor sha1024
*/
public class ConcreateProduct2 extends Product {
@Override
public void method2() {
/**
* 業務邏輯
*/
}
}
抽象工廠
/**
* @atuhor sha1024
*/
public abstract class Creator {
/**
* 創建一個產品對象,其輸入任何參數類型都可以自行設置
* 通常爲String,Enum,Class,也可以爲空
*/
public abstract <T extends Product> T createProduct(Class<T> c);
}
具體如何產生一個產品對 對象是由具體的工廠實現類來決定的
/**
* 通過發射獲取到實例
* @atuhor sha1024
*/
public class ConcreateCreator extends Creator{
@Override
public <T extends Product> T createProduct(Class<T> c) {
Product product = null;
try{
product = (T)Class.forName(c.getName()).newInstance();
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
return (T)product;
}
}
場景類
/**
* @atuhor sha1024
*/
public class Client {
public static void main(String[] args){
Creator creator = new ConcreateCreator();
Product product = creator.createProduct(ConcreateProduct1.class);
/**
* 業務邏輯
*/
product.method2();
}
}
工廠模式的優點:
- 良好的封裝性,代碼結果清晰。
- 工廠方法模式的擴展性非常好。
- 屏蔽產品類,產品類如何變化,調用者不用關心,它只需要關心產品的接口。更好的實現瞭解耦合。
工廠方法模式使用場景
- 工廠方法是new一個對象的 替代品,所以在所有需要生成對象的地方都可以使用,但是要考慮是否要增加一個產品類來增加代碼的複雜性。
- 需要靈活的可擴展的框架時,可以考慮採用工廠方法模式。
工廠模式的擴展
縮小爲簡單工廠模式
上圖中我們去掉了AbstarctHummanFactory
抽象人類工廠,同時把createHuman改爲靜態類型。
/**
* @atuhor sha1024
*/
public class NvWa {
public static void main(String[] args){
Human human = HummanFactory2.createHuman(YellowHuman.class);
Human human2 = HummanFactory2.createHuman(BlackHuman.class);
}
}
運行結果沒用發生變化,但是我們的類圖變得更加簡單了,因爲簡單所以我們稱爲簡單工廠模式
升級爲多個工廠模式
當我們在做一個複雜項目的時候,經常會遇到初始化一個對象很費力的情況,所有產品都放到一個工廠方法中進行初始化會使代嗎結構不清晰。例如:一個產品類有5個具體實現類,每個實現類的初始化方法都不相同,如果寫在工廠中,會導致該方法巨大無比。
考慮到要結構清晰,我們就爲每個產品定義一個創造者,然後由調用者去選擇與哪個工廠方法關聯。
定義一個抽象工廠
public abstract class AbstarctHummanFactory {
public abstract Human createHuman();
}
黑人人種創建工廠
public class BlackHummanFactory extends AbstarctHummanFactory {
@Override
public Human createHuman() {
return new BlackHuman();
}
}
黃色人種創建工廠
public class YellowHummanFactory extends AbstarctHummanFactory {
@Override
public Human createHuman() {
return new YellowHuman();
}
}
場景類
public static void main(String[] args){
Human yellowHuman = (new YellowHummanFactory()).createHuman();
yellowHuman.getTalk();
yellowHuman.getColor();
Human blackHuman = (new BlackHummanFactory()).createHuman();
blackHuman.getTalk();
blackHuman.getColor();
}
運行結果還是相同的,每一個產品類都對應了一個創建類好處就是創建類職責清晰,而且結構簡單,但是給可擴展性和維護性帶來了一定的影響。
當然,在複雜的應用場景中一般採用多工廠模式,然後增加一個協調類,避免調用者和各個子工廠交流,對高層模塊提供統一調用api。
替代單例模式
Singleton定義了一個private 的無參構造函數,目的是不允許通過new 的方式創建一個對象
public class Singleton {
private Singleton(){}
public void doSomeThing(){
//業務邏輯
}
}
單例類,Singleton保證不能通過正常情況下建立一個對象,只能通過反射方式創建
public class SingletonFactory {
private static Singleton singleton;
static {
try{
Class c1 = Class.forName(Singleton.class.getName());
//獲取無參構造函數
Constructor constructor = c1.getDeclaredConstructor();
//設置無參是可訪問的
constructor.setAccessible(true);
//產生一個實例對象
singleton = (Singleton) constructor.newInstance();
}catch(Exception e){
}
}
public static Singleton getSingleton(){
return singleton;
}
通過獲得類構造器,然後設置訪問權限,生成一個對象,然後提供外部訪問,保證內部中的對象唯一。當然,其他的類也能通過反射的方式獲得一個單例對象。
延遲初始化
ProductFactory負責產品類對象的創建,並通過prMap創建一個緩存,對需要被重用的對象保留
public class ProductFactory {
private static final Map<String,Product> prMap = new HashMap<>();
public static synchronized Product createProduct(String type) throws Exception{
Product product = null;
//判斷緩存中是否有這個對象
if(prMap.containsKey(type)){
product = prMap.get(type);
}else{
if(type.equals("Product1")){
product = new ConcreateProduct1();
}else{
product = new ConcreateProduct2();
}
//同時緩存數據到prMap中
prMap.put(type,product);
}
return product;
}
}
通過定義一個Map容器,容納所有對象,如果Map容器中已經有的對象,則直接取出;如果沒用,則根據需要的類型產生一個對象並放入到Map容器中。