筆記一:簡單工廠

定義:

由一個工廠對象決定決定創建出哪一種產品類的實例

類型:

創建型,不屬於GOF23種設計模式

適用場景:

​ 工廠類負責創建的 對象比較少;

​ 應用層只知道傳入工廠類的參數,對於如何創建對象不關心。

優點:

​ 只需要傳入一個正確的參數,就可以獲取你所需要的對象,而無需知道其創建細節。

​ 【實現責任分割,提供參數創建對象,客戶端(應用層)甚至可以不用知道產品類類名】

缺點:

​ 工廠類的職責相對過重,增加新的產品需要修改工廠類的判斷邏輯,違背開閉原則;

​ 無法形成基於繼承的等級結構。

代碼舉例:

業務場景:某網站有各種類型的視頻
源碼:點擊查看源碼
代碼位置:com.ljm.design.pattern.creational.simplefactory

創建一個視頻類: Video,有一個生產視頻的抽象方法

public abstract class Video {
    //視頻的屬性暫時忽略
    //有一個產生視頻的方法,因爲有各類視頻,所以是一個抽象方法
    public abstract void produce();
}

創建一個java視頻的實現類JavaVideo繼承Video,

public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("生產java課程視頻");
    }
}

一個Python視頻的實現類PythonVideo繼承Video

public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("生產Python課程視頻");
    }
}

編寫測試類Test,模擬應用層。

public class Test {
    public static void main(String[] args) {
        //現在需要一個java課程
        父類引用指向子類對象,調用子類重寫的方法
        現在的應用層是非常依賴於JavaVideo的
        Video viedo = new JavaVideo();
        viedo.produce();
    }
}

​ 需要一個java課程,父類引用指向子類實現,調用子類重寫的方法,現在的應用層是非常依賴於JavaVideo的。那麼能不能不讓應用層代碼依賴對應的類,把整個生產實例的過程放到一個類裏面,使應用層不依賴對應的具體實現類。這就是簡單工廠的由來

​ 改進:是應用層不依賴於集體的某個實現類,而是通過一個工廠類,來實現生產視頻的過程。創建生產視頻的工廠類:VideoFactory:

public class VideoFactory {
    /**
     *  創建一個生產視頻的方法
     *  根據type判斷要創建的課程
     *  v1.0
     */
    public Video getVideo(String type){     //也可以定義爲靜態方法
        if ("java".equalsIgnoreCase(type)){
            return new JavaVideo();
        } else if("Python".equalsIgnoreCase(type)){
            return new PythonVideo();
        } else {
            return null;
        }
    } 
}

使用工廠創建之前,查看一下類圖,等等好做比較:

工廠創建之前UML

註釋前面的測試代碼,重新編寫測試代碼,現在不是new課程出來,而使用剛剛寫的工廠類:

/**
 * 工廠方式
 * 不在依賴於某個具體課程
 */
VideoFactory factory = new VideoFactory();
Video video = factory.getVideo("java");
if (video == null){
    return;
}
video.produce();

在查看一下最新的UML類圖:

使用工廠後UML

應用層只依賴VideoFactory,具體的JavaVideo或者PythonVideo由VideoFactory根據參數負責創建

這種方式缺點:隨着要生產的課程種類越來越多,工廠裏的方法要不斷修改,不符合開閉原則,增加錯誤概率

改進: 利用反射彌補擴展性,重新定義VideoFactory內的getVideo 方法

/**
 * 生產視頻的方法
 * 利用反射原理
 * @param c 要生產視頻的Class對象
 * @return 生產出的課程
 */
public Video getVideo(Class c){
    Video video = null;
    try {
        video = (Video) Class.forName(c.getName()).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return video;
}

註釋前面的測試代碼,重新編寫測試代碼:

/**
 * 測試:反射方式創建需要的課程
 */
VideoFactory factory = new VideoFactory();
Video video = factory.getVideo(PythonVideo.class);
video.produce();

JDK(1.7)源碼分析:

java.util.Calendar 類下的getInstance方法。
 /**
     * Gets a calendar using the default time zone and locale. The
     * <code>Calendar</code> returned is based on the current time
     * in the default time zone with the default locale.
     *
     * @return a Calendar.
     */
    public static Calendar getInstance()
    {
        Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
        cal.sharedZone = true;
        return cal;
    }

進入 createCalendar方法:

private static Calendar createCalendar(TimeZone zone,
                                       Locale aLocale)
{
    Calendar cal = null;

    String caltype = aLocale.getUnicodeLocaleType("ca");
    if (caltype == null) {
        // Calendar type is not specified.
        // If the specified locale is a Thai locale,
        // returns a BuddhistCalendar instance.
        //這裏和VideoFactory原來的寫法異曲同工的
        if ("th".equals(aLocale.getLanguage())
                && ("TH".equals(aLocale.getCountry()))) {
            cal = new BuddhistCalendar(zone, aLocale);
        } else {
            cal = new GregorianCalendar(zone, aLocale);
        }
    } else if (caltype.equals("japanese")) {
        cal = new JapaneseImperialCalendar(zone, aLocale);
    } else if (caltype.equals("buddhist")) {
        cal = new BuddhistCalendar(zone, aLocale);
    } else {
        // Unsupported calendar type.
        // Use Gregorian calendar as a fallback.
        cal = new GregorianCalendar(zone, aLocale);
    }

    return cal;
}

按住Shift+Ctrl+Alt+U查看類圖 ----> 按住Ctrl+Alt+B經他的子類都依次顯示出來:

在這裏插入圖片描述

這是學習一些源碼的方式

其他源碼

先在pom導入依賴,詳情見pom.xml

再來看一下logback裏面簡單工廠的使用,先打開LoggerFactory,找到裏面的重載方法getLogger

/**
 * Return a logger named according to the name parameter using the statically
 * bound {@link ILoggerFactory} instance.
 *
 * @param name The name of the logger.
 * @return logger
 */
public static Logger getLogger(String name) {
  ILoggerFactory iLoggerFactory = getILoggerFactory();
  return iLoggerFactory.getLogger(name);
}

/**
 * Return a logger named corresponding to the class passed as parameter, using
 * the statically bound {@link ILoggerFactory} instance.
 *
 * @param clazz the returned logger will be named after clazz
 * @return logger
 */
public static Logger getLogger(Class clazz) {
  return getLogger(clazz.getName());
}

getLogger(Class clazz)也是調用的getLogger(String name)方法,getLogger(String name)中調用的是ILoggerFactory的getLogger方法,查看ILoggerFactory:

public interface ILoggerFactory {
  public Logger getLogger(String name);
}

他是一個接口,下面是一個方法(這個是工廠方法,先跳過),回到getLogger方法,這裏LoggerFactory肯定有多個實現,選中getLogger方法,Ctrl+Alt+B查看他的實現,這裏選擇其中一個實現類LoggerContext,

//調用的下面的方法
public final Logger getLogger(final Class clazz) {
  return getLogger(clazz.getName());
}
//傳入一個name,返回Logger
public final Logger getLogger(final String name) {

  if (name == null) {
    throw new IllegalArgumentException("name argument cannot be null");
  }

  //返回root節點(用於檢索根記錄器的名稱)的logger
  if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
    return root;
  }

  int i = 0;
  Logger logger = root;
  //檢查所需的記錄器是否存在,如果存在,則返回它
  Logger childLogger = (Logger) loggerCache.get(name);
  if (childLogger != null) {
    return childLogger;
  }

  String childName;
  while (true) {
    int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
    if (h == -1) {
      childName = name;
    } else {
      childName = name.substring(0, h);
    }

    i = h + 1;
    synchronized (logger) {
      childLogger = logger.getChildByName(childName);
      if (childLogger == null) {
        childLogger = logger.createChildByName(childName);
        loggerCache.put(childName, childLogger);
        incSize();
      }
    }
    logger = childLogger;
    if (h == -1) {
      return childLogger;
    }
  }
}

這裏也是根據傳入的參數name,選擇要返回的Logger

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章