工廠模式為創(chuàng)建對象提供了接口甩骏,把對象的創(chuàng)建交給工廠來處理,而不是我們主動的去new。一方面可以屏蔽對象創(chuàng)建的邏輯根蟹,另一方面當(dāng)某一個在應(yīng)用中到處傳播的對象需要修改實例化的邏輯時蕾域,我們只需要修改其工廠代碼拷肌。
工廠模式可以分為以下三類
- 簡單工廠
- 工廠模式
- 抽象工廠
其中簡單工廠并不屬于23種設(shè)計模式
簡單工廠
顧名思義,簡單工廠相比其他兩種工廠類的模式相對簡單不少旨巷。它是由一個工廠對象來決定創(chuàng)建出哪一種產(chǎn)品的示例
下面巨缘,我們以生產(chǎn)Cpu的例子來進(jìn)行講解
- 首先定義一個生產(chǎn)Cpu的接口
public interface Cpu
{
void produce();
}
- 分別定義其不同的實現(xiàn)類
public class KirinCpu implements Cpu
{
@Override
public void produce()
{
System.out.println("華為麒麟處理器");
}
}
public class MtkCpu implements Cpu
{
@Override
public void produce()
{
System.out.println("聯(lián)發(fā)科處理器");
}
}
public class SnapdragonCpu implements Cpu
{
@Override
public void produce()
{
System.out.println("高通驍龍?zhí)幚砥?);
}
}
- 創(chuàng)建一個生產(chǎn)Cpu的工廠類,根據(jù)傳入的參數(shù)返回一個對應(yīng)的對象
public class CpuFactory
{
public static Cpu getCpu(String type)
{
if(type.equals("Kirin"))
{
return new KirinCpu();
}else if(type.equals("Mtk"))
{
return new MtkCpu();
}else if(type.equals("Snapdragon"))
{
return new SnapdragonCpu();
}
return null;
}
}
4.這樣采呐,我們在客戶端應(yīng)用層就可以直接通過工廠來獲取想要的Cpu
public class Client
{
public static void main(String[] args)
{
Cpu cpu = CpuFactory.getCpu("Mtk");
if(cpu != null)
{
cpu.produce();
}
}
}
如果不采用簡單工廠若锁,我們需要在我們的應(yīng)用層來處理產(chǎn)品類的具體實現(xiàn),而采用簡單工廠后斧吐,產(chǎn)品類的具體實現(xiàn)放在了工廠里拴清。這樣就對我們的應(yīng)用層進(jìn)行了解耦
缺點
- 工廠類的職責(zé)相對較重,所有產(chǎn)品的實現(xiàn)都在一個工廠方法里執(zhí)行
- 不符合開閉原則(即對修改關(guān)閉会通,擴(kuò)展開放)口予,當(dāng)我們新增一個產(chǎn)品的實現(xiàn)類的時候,需要對工廠方法進(jìn)行修改
改進(jìn)
我們可以通過反射來改進(jìn)我們的工廠方法
public class CpuFactory
{
public static Cpu getCpu(Class<? extends Cpu> c)
{
Cpu cpu = null;
try
{
cpu = c.newInstance();
} catch (InstantiationException | IllegalAccessException e)
{
e.printStackTrace();
}
return cpu;
}
}
簡單工廠模式的應(yīng)用
jdk中Calendar類的getInstance()方法
其有以上的重載方法涕侈,但最終都會調(diào)用createCalendar(TimeZone zone,Locale aLocale)方法來生成一個Calendar對象沪停,如果指定TimeZone和Locale則會把指定的值作為參數(shù)傳遞給createCalendar方法,如果沒有則先獲得系統(tǒng)默認(rèn)值然后再傳遞裳涛。
其方法聲明如下
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
Calendar類有以下實現(xiàn)
我們可以發(fā)現(xiàn)在
createCalendar
方法內(nèi)通過對傳入的TimeZone
和Locale
的值進(jìn)行判斷木张,來生成不同的Calendar
對象
jdbc中數(shù)據(jù)庫連接的獲取
我們在寫jdbc程序時,首先進(jìn)行的是Class.forName()
注冊驅(qū)動端三,然后通過DriverManager.getConnection
方法來獲取數(shù)據(jù)庫的連接舷礼。
在這里以mysql為例。當(dāng)執(zhí)行完Class.forName("com.mysql.jdbc.Driver")
郊闯,會完成類的加載妻献,而在類加載初始化過程中會對靜態(tài)代碼塊里的內(nèi)容進(jìn)行執(zhí)行,打開其源碼团赁,我們發(fā)現(xiàn)其在里面完成了驅(qū)動的注冊
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
在DriverMananger
里有以下一行代碼聲明
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
而DriverManager.registerDriver(new Driver())
方法會生成一個DriverInfo
育拨,然后將其添加到此CopyOnWriteArrayList里面,至此欢摄,驅(qū)動的注冊完成熬丧。
而對于DriverManager.getConnection
,雖然其由好幾種重載方法怀挠,但最終會都是調(diào)用getConnection(String url, java.util.Properties info, Class<?> caller)
方法析蝴,打開其源碼
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
我們會發(fā)現(xiàn)其通過遍歷已經(jīng)注冊的驅(qū)動害捕,如果驅(qū)動正常,則直接返回該驅(qū)動生成的
Connection闷畸。
在這里驅(qū)動的注冊相當(dāng)于工廠方法的參數(shù)的傳遞尝盼,然后就可以通過getConnection
方法獲得該驅(qū)動所對應(yīng)的連接