一、概念:
設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用蹈集、多數(shù)人知曉的、經(jīng)過分類編目的代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)减响,是一種用于固定場合的固定套路
基本分類:
- 創(chuàng)建型模式:單例設(shè)計(jì)模式,工廠方法模式支示,抽象工廠模式
- 結(jié)構(gòu)性模式:裝飾器模式,代理模式
- 行為型模式:模板設(shè)計(jì)模式
二促绵、設(shè)計(jì)模式詳解:
- 單例設(shè)計(jì)模式:懶漢式嘴纺,餓漢式。懶漢式需要對多線程進(jìn)行同步處理
- 餓漢式:(立即加載)使用類的時候就創(chuàng)建實(shí)例
public class Singleton {
// 將自身實(shí)例化對象設(shè)置為一個屬性栽渴,并用static、final修飾
private static final Singleton instance = new Singleton();
// 構(gòu)造方法私有化
private Singleton() {}
// 靜態(tài)方法返回該實(shí)例
public static Singleton getInstance() {
return instance;
}
}
優(yōu)點(diǎn):實(shí)現(xiàn)起來簡單糖驴,沒有多線程同步問題佛致。
缺點(diǎn):當(dāng)類SingletonTest被加載的時候,會初始化static的instance俺榆,靜態(tài)變量被創(chuàng)建并分配內(nèi)存空間,從這以后定嗓,這個static的instance對象便一直占著這段內(nèi)存(即便你還沒有用到這個實(shí)例)萍桌,當(dāng)類被卸載時宵溅,靜態(tài)變量被摧毀上炎,并釋放所占有的內(nèi)存,因此在某些特定條件下會耗費(fèi)內(nèi)存寇损。
- 懶漢式:(延遲加載)當(dāng)調(diào)用get方法時裳食,再創(chuàng)建實(shí)例
public class Singleton {
// 將自身實(shí)例化對象設(shè)置為一個屬性,并用static修飾
private static Singleton instance;
// 構(gòu)造方法私有化
private Singleton() {}
// 靜態(tài)方法返回該實(shí)例
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
優(yōu)點(diǎn):實(shí)現(xiàn)起來比較簡單诲祸,當(dāng)類SingletonTest被加載的時候憨愉,靜態(tài)變量static的instance未被創(chuàng)建并分配內(nèi)存空間卿捎,當(dāng)getInstance方法第一次被調(diào)用時,初始化instance變量躺孝,并分配內(nèi)存底桂,因此在某些特定條件下會節(jié)約了內(nèi)存。
缺點(diǎn):存在線程安全問題
- 線程安全的懶漢式:
public class Singleton {
// 將自身實(shí)例化對象設(shè)置為一個屬性籽懦,并用static修飾
private static Singleton instance;
// 構(gòu)造方法私有化
private Singleton() {}
// 靜態(tài)方法返回該實(shí)例,加synchronized關(guān)鍵字實(shí)現(xiàn)同步
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
優(yōu)點(diǎn):在多線程的情況下厅篓,使用synchronized方法保證線程安全
缺點(diǎn):效率低下
- 最優(yōu)懶漢式:
public class Singleton {
// 將自身實(shí)例化對象設(shè)置為一個屬性捶码,并用static修飾
private static Singleton instance;
// 構(gòu)造方法私有化
private Singleton() {}
// 靜態(tài)方法返回該實(shí)例
public static Singleton getInstance() {
// 第一次檢查instance是否被實(shí)例化出來,如果沒有進(jìn)入if塊
if(instance == null) {
synchronized (Singleton.class) {
// 某個線程取得了類鎖档押,實(shí)例化對象前第二次檢查instance是否已經(jīng)被實(shí)例化出來祈纯,如果沒有令宿,才最終實(shí)例出對象
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
-
普通工廠模式:建立一個工廠類粒没,對實(shí)現(xiàn)了同一接口的不同實(shí)現(xiàn)類進(jìn)行實(shí)例的創(chuàng)建
/**
* 發(fā)送郵件類
*/
public class MailSender implements Sender{
@Override
public void send() {
System.out.println("正在發(fā)送郵件...");
}
}
/**
* 發(fā)送短信類
*/
public class SmsSender implements Sender{
@Override
public void send() {
System.out.println("正在發(fā)送短信...");
}
}
/**
* 工廠類
*/
public class SendFactory {
// 自定義成員方法實(shí)現(xiàn)對象的創(chuàng)建
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
return null;
}
}
/**
* 測試類
*/
public class SendFactoryTest {
public static void main(String[] args) {
// 1. 聲明工廠類型的引用指向工廠類型的對象
SendFactory sf = new SendFactory();
// 2. 調(diào)用生產(chǎn)方法來實(shí)現(xiàn)對象的創(chuàng)建
Sender sender = sf.produce("mail");
// 3. 使用對象調(diào)用方法模擬發(fā)生的行為
sender.send();
}
}
主要缺點(diǎn):在普通工廠方法模式中油昂,如果傳遞的字符串出錯,則不能正確創(chuàng)建對象,并可能出現(xiàn)空指針異常
優(yōu)點(diǎn):在創(chuàng)建大量對象的前提下匆浙,擴(kuò)展性和可維護(hù)性強(qiáng)
-
多個工廠模式:
/**
* 工廠類
*/
public class SendFactory {
// 自定義成員方法實(shí)現(xiàn)對象的創(chuàng)建
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
return null;
}
// 自定義對應(yīng)的成員方法創(chuàng)建對應(yīng)的對象
public Sender produceMail() {
return new MailSender();
}
public Sender produceSms() {
return new SmsSender();
}
}
/**
* 測試類
*/
public class SendFactoryTest {
public static void main(String[] args) {
// 1. 聲明工廠類型的引用指向工廠類型的對象
SendFactory sf = new SendFactory();
// 2. 調(diào)用生產(chǎn)方法來實(shí)現(xiàn)對象的創(chuàng)建
//Sender sender = sf.produce("mail");
Sender sender = sf.produceMail();
// 3. 使用對象調(diào)用方法模擬發(fā)生的行為
sender.send();
}
}
主要缺點(diǎn):在多個工廠方法模式中首尼,為了能夠正確創(chuàng)建對象言秸,先需要創(chuàng)建工廠類的對象才能調(diào)用工廠類中的生產(chǎn)方法
-
靜態(tài)工廠模式:
/**
* 工廠類
*/
public class SendFactory {
// 自定義成員方法實(shí)現(xiàn)對象的創(chuàng)建
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
return null;
}
// 自定義對應(yīng)的靜態(tài)成員方法創(chuàng)建對應(yīng)的對象
public static Sender produceMail() {
return new MailSender();
}
public static Sender produceSms() {
return new SmsSender();
}
}
/**
* 測試類
*/
public class SendFactoryTest {
public static void main(String[] args) {
// 1. 聲明工廠類型的引用指向工廠類型的對象
//SendFactory sf = new SendFactory();
// 2. 調(diào)用生產(chǎn)方法來實(shí)現(xiàn)對象的創(chuàng)建
//Sender sender = sf.produce("mail");
//Sender sender = sf.produceMail();
Sender sender = SendFactory.produceMail();
// 3. 使用對象調(diào)用方法模擬發(fā)生的行為
sender.send();
}
}
實(shí)際意義:適合于出現(xiàn)了大量的產(chǎn)品需要創(chuàng)建且它們具有共同的接口時举畸,可以通過工廠方法模式進(jìn)行創(chuàng)建
主要缺點(diǎn):工廠方法模式有一個問題就是凳枝,類的創(chuàng)建依賴工廠類抄沮,如果想要拓展程序生產(chǎn)新的產(chǎn)品岖瑰,就必須對工廠的代碼進(jìn)行修改,就違背了開閉原則
-
抽象工廠模式:
/**
* 工廠接口類
*/
public interface Provider {
// 自定義抽象方法描述產(chǎn)品的生產(chǎn)行為
Sender produce();
}
/**
* 發(fā)送郵件的工廠類
*/
public class MailSendFactory implements Provider{
@Override
public Sender produce() {
return new MailSender();
}
}
/**
* 發(fā)送短信的工廠類
*/
public class SmsSendFactory implements Provider{
@Override
public Sender produce() {
return new SmsSender();
}
}
/**
* 測試類
*/
public class SendFactoryTest {
public static void main(String[] args) {
Provider provider = new MailSendFactory();
Sender sender = provider.produce();
sender.send();
}
}
優(yōu)點(diǎn):當(dāng)需要生產(chǎn)新的產(chǎn)品時率挣,只需要創(chuàng)建對應(yīng)的工廠類即可
-
裝飾器模式:給一個對象動態(tài)地增加一些新功能露戒,要求裝飾對象和被裝飾對象實(shí)現(xiàn)同一個接口,裝飾對象持有被裝飾對象的實(shí)例
/**
* 定義接口
*/
public interface Sourceable {
// 自定義抽象方法
void method();
}
/**
* 定義原本的類
*/
public class Source implements Sourceable{
@Override
public void method() {
System.out.println("原本的類蛾茉!");
}
}
/**
* 裝飾類
*/
public class Decorator implements Sourceable{
private Sourceable source;
public Decorator(Sourceable source) {
this.source = source;
}
@Override
public void method() {
// 保證原有功能
source.method();
// 添加新的功能
System.out.println("我是裝飾器撩鹿!");
}
}
/**
* 測試類
*/
public class SourceableTest {
public static void main(String[] args) {
// 使用原有類實(shí)現(xiàn)功能
Sourceable sourceable = new Source();
sourceable.method();
System.out.println("------------------------------");
// 使用裝飾類實(shí)現(xiàn)功能
Sourceable sourceable1 = new Decorator(sourceable);
sourceable1.method();
}
}
實(shí)際意義:
- 可以實(shí)現(xiàn)一個類功能的擴(kuò)展
- 可以動態(tài)地增加功能,而且還能動態(tài)撤銷
缺點(diǎn):產(chǎn)生過多相似的對象键思,不易排錯
-
代理模式:找一個代理類甫贯,替原對象進(jìn)行一些操作
/**
* 代理類
*/
public class Proxy implements Sourceable{
private Source source;
public Proxy() {
source = new Source();
}
@Override
public void method() {
source.method();
System.out.println("我是代理模式!");
}
}
/**
* 測試類
*/
public class SourceableTest {
public static void main(String[] args) {
// 使用原有類實(shí)現(xiàn)功能
Sourceable sourceable = new Source();
sourceable.method();
System.out.println("------------------------------");
// 使用代理類實(shí)現(xiàn)功能
Sourceable sourceable1 = new Proxy();
sourceable1.method();
}
}
實(shí)際意義:
- 如果在使用的時候需要對原有的方法進(jìn)行改進(jìn)赔桌,可以采用一個代理類調(diào)用原有方法渴逻,并且對產(chǎn)生的結(jié)果進(jìn)行控制
- 使用代理模式疾党,可以將功能劃分得更加清晰惨奕,有助于后期維護(hù)
與裝飾器模式的比較:
裝飾器模式通常的做法是將原始對象作為一個參數(shù)傳給裝飾者的構(gòu)造器,而代理模式通常在一個代理類中創(chuàng)建一個被代理類的對象
裝飾器模式關(guān)注于在一個對象上動態(tài)的添加方法雹洗,而代理模式關(guān)注于控制對對象的訪問
-
模板模式:指一個抽象類中封裝了一個固定流程,流程中的具體步驟可以由不同子類進(jìn)行不同的實(shí)現(xiàn)时肿,通過抽象類讓固定的流程產(chǎn)生不同的結(jié)果
/**
* 定義一個抽象類
*/
public abstract class AbstractCalculator {
// 自定義成員方法實(shí)現(xiàn)將參數(shù)指定的表達(dá)式按照參數(shù)指定的規(guī)則進(jìn)行切割并返回計(jì)算結(jié)果
public int splitExpression(String exp, String op) {
String[] sArr = exp.split(op);
return calculate(Integer.parseInt(sArr[0]), Integer.parseInt(sArr[1]));
}
// 自定義抽象方法實(shí)現(xiàn)運(yùn)算
public abstract int calculate(int ia, int ib);
}
/**
* 定義加法
*/
public class Plus extends AbstractCalculator{
@Override
public int calculate(int ia, int ib) {
return ia + ib;
}
}
/**
* 定義減法
*/
public class Minus extends AbstractCalculator{
@Override
public int calculate(int ia, int ib) {
return ia - ib;
}
}
/**
* 測試類
*/
public class AbstractCalculatorTest {
public static void main(String[] args) {
AbstractCalculator abstractCalculator = new Plus();
int res = abstractCalculator.splitExpression("1+1", "\\+");
System.out.println("最終的運(yùn)算結(jié)果是:" + res);
}
}
實(shí)際意義:
- 將多個子類共有且邏輯基本相同的內(nèi)容提取出來實(shí)現(xiàn)代碼復(fù)用
- 不同的子類實(shí)現(xiàn)不同的效果形成多態(tài)螃成,有助于后期維護(hù)