常用的設(shè)計(jì)原則
-
開閉原則(Open Close Principle)
對(duì)擴(kuò)展開放對(duì)修改關(guān)閉,為了使程序的擴(kuò)展性好,易于維護(hù)和升級(jí)。
-
里氏代換原則(Liskov Substitution Principle)
任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn),多使用多態(tài)的方式。
-
依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)
盡量多依賴于抽象類或接口而不是具體實(shí)現(xiàn)類,對(duì)子類具有強(qiáng)制性和規(guī)范性
-
接口隔離原則(Interface Segregation Principle)
盡量多使用小接口而不是大接口,避免接口的污染,降低類之間耦合度。
-
迪米特法則(最少知道原則)(Demeter Principle)
一個(gè)實(shí)體應(yīng)當(dāng)盡量少與其他實(shí)體之間發(fā)生相互作用,使系統(tǒng)功能模塊相對(duì)獨(dú)立。高內(nèi)聚,低耦合。
-
合成復(fù)用原則(Composite Reuse Principle)
盡量多使用合成/聚合的方式,而不是繼承的方式。
常用的設(shè)計(jì)模式
設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。
設(shè)計(jì)模式就是一種用于固定場(chǎng)合的固定套路。
- 創(chuàng)建型模式 - 單例設(shè)計(jì)模式、工廠方法模式、抽象工廠模式、建造者模式、原型模式
- 結(jié)構(gòu)型模式 - 裝飾器模式、代理模式、適配器模式、橋接模式、組合模式、外觀模式、享元模式
- 行為型模式 - 模板設(shè)計(jì)模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、接解釋器模式、狀態(tài)模式、策略模式、職責(zé)鏈模式、訪問(wèn)者模式。
單例設(shè)計(jì)模式
單例設(shè)計(jì)模式主要分為:餓漢式 和 懶漢式,懶漢式需要對(duì)多線程進(jìn)行同步處理。
餓漢模式 (立即加載, 線程安全, 推薦):類加載慢, 運(yùn)行時(shí)獲取對(duì)象快
/*
餓漢式
*/
public class Singleton {
// 2. 使用private static 共同修飾對(duì)象的引用
private static Singleton sin = new Singleton();
//1. 私有化構(gòu) 方法 使用private關(guān)鍵字修飾
private Singleton(){ }
// 3. 提供公有的get方法將對(duì)象返回出去,用public static共同修飾
public static Singleton getInstance(){
return sin;
}
}
懶漢模式 (延遲加載, 非線程安全):類加載快, 運(yùn)行時(shí)獲取對(duì)象慢
/*
懶漢式--雙重鎖(適用于多線程)
*/
public class Singleton {
// 2.聲明本類類型的引用指向本類類型的對(duì)象并使用private static關(guān)鍵字修飾
private static Singleton sin = null;
// 1.私有化構(gòu)造方法,使用private關(guān)鍵字修飾
private Singleton() {}
// 3.提供公有的get方法負(fù)責(zé)將上述對(duì)象返回出去,使用public static關(guān)鍵字修飾
public static Singleton getInstance() {
if (null == sin) {
synchronized (Singleton.class) {
if (null == sin) {
sin = new Singleton();
}
}
}
return sin;
}
}
工廠方法模式
-
普通工廠模式:
普通工廠方法模式就是建立一個(gè)工廠類,對(duì)實(shí)現(xiàn)了同一接口的不同實(shí)現(xiàn)類進(jìn)行實(shí)例的創(chuàng)建。
Send接口:
public interface Sender {
// 自定義抽象方法來(lái)描述發(fā)送的行為
void send();
}
Send實(shí)現(xiàn)類:
/*
短信形式發(fā)送...
*/
public class SmsSender implements Sender {
@Override
public void send() {
System.out.println("正在發(fā)送短信...");
}
}
/*
郵件形式發(fā)送...
*/
public class MailSender implements Sender {
@Override
public void send() {
System.out.println("正在發(fā)送郵件...");
}
}
普通工廠:
public class SendFactory {
// 自定義成員方法實(shí)現(xiàn)對(duì)象的創(chuàng)建
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
return null;
}
測(cè)試:
public class SendFactoryTest {
public static void main(String[] args) {
// 缺點(diǎn):代碼復(fù)雜,可讀性略差
// 優(yōu)點(diǎn):擴(kuò)展性和可維護(hù)性更強(qiáng)! 尤其是在創(chuàng)建大量對(duì)象的前提下
// 1.聲明工廠類類型的引用指向工廠類類型的對(duì)象
SendFactory sf = new SendFactory();
// 2.調(diào)用生產(chǎn)方法來(lái)實(shí)現(xiàn)對(duì)象的創(chuàng)建
Sender sender = sf.produce("mail");
// 3.使用對(duì)象調(diào)用方法模擬發(fā)生的行為
sender.send(); // 輸出:正在發(fā)送郵件...
}
}
主要缺點(diǎn) :在普通工廠方法模式中,如果傳遞的字符串出錯(cuò),則不能正確創(chuàng)建對(duì)象,并且可能出現(xiàn)空指針異常。
-
多個(gè)工廠方法模式:
多個(gè)工廠:
public class SendFactory {
// 自定義成員方法實(shí)現(xiàn)對(duì)象的創(chuàng)建
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
return null;
}
public Sender produceMail() {
return new MailSender();
}
public Sender produceSms() {
return new SmsSender();
}
}
測(cè)試:
public class SendFactoryTest {
public static void main(String[] args) {
// 缺點(diǎn):代碼復(fù)雜,可讀性略差
// 優(yōu)點(diǎn):擴(kuò)展性和可維護(hù)性更強(qiáng)! 尤其是在創(chuàng)建大量對(duì)象的前提下
// 1.聲明工廠類類型的引用指向工廠類類型的對(duì)象
SendFactory sf = new SendFactory();
// 2.調(diào)用生產(chǎn)方法來(lái)實(shí)現(xiàn)對(duì)象的創(chuàng)建
Sender sender = sf.produceMail; //多個(gè)工廠
// 3.使用對(duì)象調(diào)用方法模擬發(fā)生的行為
sender.send(); // 輸出:正在發(fā)送郵件...
}
}
主要缺點(diǎn) :在多個(gè)工廠方法模式中,為了能夠正確創(chuàng)建對(duì)象,先需要?jiǎng)?chuàng)建工廠類的對(duì)象才能調(diào)用工廠類中的生產(chǎn)方法。
-
靜態(tài)工廠方法模式:
靜態(tài)工廠:
public class SendFactory {
// 自定義成員方法實(shí)現(xiàn)對(duì)象的創(chuàng)建
public Sender produce(String type) {
//System.out.println("隨便加一句打印進(jìn)行測(cè)試");
if ("mail".equals(type)) {
return new MailSender();
}
if ("sms".equals(type)) {
return new SmsSender();
}
return null;
}
public static Sender produceMail() {
return new MailSender();
}
public static Sender produceSms() {
return new SmsSender();
}
}
測(cè)試:
public class SendFactoryTest {
public static void main(String[] args) {
// 缺點(diǎn):代碼復(fù)雜,可讀性略差
// 優(yōu)點(diǎn):擴(kuò)展性和可維護(hù)性更強(qiáng)! 尤其是在創(chuàng)建大量對(duì)象的前提下
// 1.聲明工廠類類型的引用指向工廠類類型的對(duì)象
//SendFactory sf = new SendFactory();
// 2.調(diào)用生產(chǎn)方法來(lái)實(shí)現(xiàn)對(duì)象的創(chuàng)建
//Sender sender = sf.produce("mail");
//Sender sender = sf.produce("maill");
//Sender sender = sf.produceMail();
Sender sender = SendFactory.produceMail(); //靜態(tài)工廠
// 3.使用對(duì)象調(diào)用方法模擬發(fā)生的行為
sender.send();
}
}
工廠方法模式適合:凡是出現(xiàn)了大量的產(chǎn)品需要?jiǎng)?chuàng)建且具有共同的接口時(shí),可以通過(guò)工廠方法模式進(jìn)行創(chuàng)建。
主要缺點(diǎn) :
工廠方法模式有一個(gè)問(wèn)題就是,類的創(chuàng)建依賴工廠類,也就是說(shuō),如果想要拓展程序生產(chǎn)新的產(chǎn)品,就必須對(duì)工廠類的代碼進(jìn)行修改,這就違背了開閉原則。
抽象工廠模式
圍繞一個(gè)超級(jí)工廠創(chuàng)建其他工廠。該超級(jí)工廠又稱為其他工廠的工廠。
抽象工廠模式提供了一個(gè)創(chuàng)建一系列相關(guān)或者相互依賴對(duì)象的接口,無(wú)需指定他們具體的類。
就拿上面的例子來(lái)講,抽象工廠模式對(duì)比上面的工廠方法模式區(qū)別就是:工廠方法模式是一個(gè)工廠類(SendFactory )中有兩個(gè)生產(chǎn)方法(produceMail()和produceSms())。而抽象工廠就是兩個(gè)工廠類各自有一個(gè)生產(chǎn)方法,然后讓兩個(gè)工廠類實(shí)現(xiàn)同一個(gè)接口。
適用場(chǎng)景:
客戶端不依賴于產(chǎn)品類實(shí)例如何被創(chuàng)建、實(shí)現(xiàn)等細(xì)節(jié)。
強(qiáng)調(diào)一系列相關(guān)的產(chǎn)品對(duì)象(屬于同一產(chǎn)品族)一起使用創(chuàng)建對(duì)象需要大量的重復(fù)代碼。
提供一個(gè)產(chǎn)品類的庫(kù),所有的產(chǎn)品以同樣的接口出現(xiàn),從而使得客戶端不依賴于具體的實(shí)現(xiàn)。
優(yōu)點(diǎn):
具體產(chǎn)品在應(yīng)用層的代碼隔離,無(wú)需關(guān)心創(chuàng)建的細(xì)節(jié)。
將一個(gè)系列的產(chǎn)品統(tǒng)一到一起創(chuàng)建。
總結(jié):
1、簡(jiǎn)單工廠模式(靜態(tài)工廠模式):雖然某種程度上不符合設(shè)計(jì)原則,但實(shí)際使用最多。
2、工廠方法模式:不修改已有類的前提上,通過(guò)增加新的工廠類實(shí)現(xiàn)擴(kuò)展。
3、抽象工廠模式:不可以增加產(chǎn)品,可以增加產(chǎn)品族。
應(yīng)用場(chǎng)景:
1、JDk中Calendar的getInstance方法
2、JDBC中的Connection對(duì)象的獲取
3、Spring中IOC容器創(chuàng)建管理bean對(duì)象
4、反射中Class對(duì)象的newInstance方法
核心本質(zhì):
實(shí)例化對(duì)象不使用new,用工廠方法替代。
將選擇實(shí)現(xiàn)類,創(chuàng)建對(duì)象統(tǒng)一管理和控制,從而將調(diào)用者跟我們的實(shí)現(xiàn)類解耦。
裝飾器模式
裝飾器模式就是給一個(gè)對(duì)象動(dòng)態(tài)的增加一些新功能,要求裝飾對(duì)象和被裝飾對(duì)象實(shí)現(xiàn)同一個(gè)接口,裝飾對(duì)象持有被裝飾對(duì)象的實(shí)例。
實(shí)際意義:
可以實(shí)現(xiàn)一個(gè)類功能的擴(kuò)展??梢詣?dòng)態(tài)的增加功能,而且還能動(dòng)態(tài)撤銷(繼承不行)。
缺點(diǎn):產(chǎn)生過(guò)多相似的對(duì)象,不易排錯(cuò)。
代理模式
代理模式就是找一個(gè)代理類替原對(duì)象進(jìn)行一些操作。
如果在使用的時(shí)候需要對(duì)原有的方法進(jìn)行改進(jìn),可以采用一個(gè)代理類調(diào)用原有方法,并且對(duì)產(chǎn)生的結(jié)果進(jìn)行控制,這種方式就是代理模式。
使用代理模式,可以將功能劃分的更加清晰,有助于后期維護(hù)。
接口:
public interface Sourceable {
// 自定義抽象方法
void method();
}
真實(shí)對(duì)象:
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("--我是真實(shí)對(duì)象,也就是被代理的對(duì)象(房東)");
}
}
代理對(duì)象:
public class Proxy implements Sourceable {
private Source source;
public Proxy() {
source = new Source();
}
@Override
public void method() {
source.method();
System.out.println("我是代理對(duì)象?。ㄖ薪椋?);
}
}
訪問(wèn)測(cè)試:
public class SourceableTest {
public static void main(String[] args) {
// 使用代理類實(shí)現(xiàn)功能(找中介租房)
Sourceable sourceable = new Proxy();
sourceable.method();
}
}
代理模式和裝飾器模式的比較:
- 裝飾器模式通常的做法是將原始對(duì)象作為一個(gè)參數(shù)傳給裝飾者的構(gòu)造器,而代理模式通常在一個(gè)代理類中創(chuàng)建一個(gè)被代理類的對(duì)象。
- 裝飾器模式關(guān)注于在一個(gè)對(duì)象上動(dòng)態(tài)的添加方法,然而代理模式關(guān)注于控制對(duì)對(duì)象的訪問(wèn)。
模板設(shè)計(jì)模式
模板方法模式主要指一個(gè)抽象類中封裝了一個(gè)固定流程,流程中的具體步驟可以由不同子類進(jìn)行不同的實(shí)現(xiàn),通過(guò)抽象類讓固定的流程產(chǎn)生不同的結(jié)果。
實(shí)際意義:
- 將多個(gè)子類共有且邏輯基本相同的內(nèi)容提取出來(lái)實(shí)現(xiàn)代碼復(fù)用。
- 不同的子類實(shí)現(xiàn)不同的效果形成多態(tài),有助于后期維護(hù)。