設計模式Design Patterns (一)
設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使程式碼編制真正工程化,設計模式是軟體工程的基石,如同大廈的一塊塊磚石一樣。專案中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。
一、設計模式的分類
總體來說設計模式分為三大類: 建立型模式,共五種:**工廠**方法模式、**抽象工廠**模式、**單例**模式、**建造者**模式、**原型**模式。 結構型模式,共七種:介面卡模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。 行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、直譯器模式。
二、設計模式的六大原則
1、**開閉原則**(Open Close Principle) 開閉原則就是說對擴充套件開放,對修改關閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程式的擴充套件性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類,後面的具體設計中我們會提到這點。 2、**里氏代換原則**(Liskov Substitution Principle) 里氏代換原則(Liskov Substitution Principle LSP)面向物件設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。 LSP是繼承複用的基石,只有當衍生類可以替換掉基類,軟體單位的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對“開-閉”原則的補充。實現“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。 3、**依賴倒轉原則**(Dependence Inversion Principle) 這個是開閉原則的基礎,具體內容:真對介面程式設計,依賴於抽象而不依賴於具體。 4、**介面隔離原則**(Interface Segregation Principle) 這個原則的意思是:使用多個隔離的介面,比使用單個介面要好。還是一個降低類之間的耦合度的意思,從這兒我們看出,其實設計模式就是一個軟體的設計思想,從大型軟體架構出發,為了升級和維護方便。所以上文中多次出現:降低依賴,降低耦合。 5、**迪米特法則**(最少知道原則)(Demeter Principle) 為什麼叫最少知道原則,就是說:一個實體應當儘量少的與其他實體之間發生相互作用,使得系統功能模組相對獨立。 6、**合成複用原則**(Composite Reuse Principle) 原則是儘量使用合成/聚合的方式,而不是使用繼承。
建立型模式
1.工廠方法模式(Factory Method)
1.1 普通工廠模式
舉例
一個sender介面和兩個實現該介面的類
public interface Sender {
public void send();
}
public class MailSender implements Sender {
@Override
public void send() {
System.out.println("this is MailSender");
}
}
public class SmsSender implements Sender {
@Override
public void send() {
System.out.println("this is SmsSender");
}
}
建立工廠類 定義一個根據不同型別返回不同實現類的方法
public class SendFactory {
public Sender produce(String type){
if("mail".equals(type)){
return new MailSender();
}else if("sms".equals(type)){
return new SmsSender();
}else{
System.out.println("請輸入正確的型別!");
return null;
}
}
}
測試類
public class FactoryTest {
public static void main(String[] args) {
SendFactory sendFactory = new SendFactory();
Sender sender = sendFactory.produce("mail");
sender.send();
}
}
在普通工廠方法模式中,如果傳遞的字串出錯,則不能正確建立物件
1.2 多個工廠方法模式
-----提供多個工廠方法,分別建立物件
舉例:
介面和實現類同上
建立的工廠類中定義了兩個不同的方法 分別返回不同的實現類
public class SendFactory {
public Sender produceSms() {
return new SmsSender();
}
public Sender produceMail() {
return new MailSender();
}
}
測試類
public class FactoryTest {
public static void main(String[] args) {
SendFactory sendFactory = new SendFactory();
Sender sender = sendFactory.produceSms();
sender.send();
}
}
1.3 **靜態工廠模式**
----- 無需建立工廠例項
在上一個模式的基礎上 將工廠類內部的方法定義為靜態static
public class SendFactory {
public static Sender produceSms() {
return new SmsSender();
}
public static Sender produceMail() {
return new MailSender();
}
}
測試類
public class FactoryTest {
public static void main(String[] args) {
Sender sender = SendFactory.produceMail();
sender.send();;
}
}
2.抽象工廠模式 Abstract Factory
------ 建立多個工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了,不需要修改之前的程式碼
舉例
定義一個Sender介面和兩個實現類MailSender & SmsSender
public interface Sender {
public void send();
}
public class MailSender implements Sender {
@Override
public void send() {
System.out.println("this is MailSender");
}
}
public class SmsSender implements Sender {
@Override
public void send() {
System.out.println("this is SmsSender");
}
}
再定義一個Provider介面和實現它的兩個工廠類MailFactory & SmsFactory
public interface Provider {
public Sender produce();
}
public class MailFactory implements Provider {
@Override
public Sender produce() {
return new MailSender();
}
}
public class SmsFactory implements Provider {
@Override
public Sender produce() {
return new SmsSender();
}
}
測試類
public class FactoryTest {
public static void main(String[] args) {
Provider provider = new MailFactory();
Sender sender = provider.produce();
sender.send();
}
}
如果你現在想增加一個功能:發及時資訊,則只需做一個實現類,實現Sender介面,同時做一個工廠類,實現Provider介面,就OK了,無需去改動現成的程式碼。這樣做,拓展性較好!
3.單例模式 Singleton
------單例物件能保證在一個JVM中,該物件只有一個例項存在
好處:
1、某些類建立比較頻繁,對於一些大型的物件,這是一筆很大的系統開銷。
2、省去了new操作符,降低了系統記憶體的使用頻率,減輕GC壓力。
3、有些類如交易所的核心交易引擎,控制著交易流程,如果該類可以建立多個的話,系統完全亂了。(比如一個軍隊出現了多個司令員同時指揮,肯定會亂成一團),所以只有使用單例模式,才能保證核心交易伺服器獨立控制整個流程。
舉例
public class Singleton {
/*持有私有靜態例項 防止被引用
* 此處賦值null==> 實現延遲載入*/
// private static Singleton instance = null;
/*私有構造方法 防止被例項化*/
private Singleton() {
}
private static class SingletonFactory{
private static Singleton instance = new Singleton();
}
/*靜態工程方法 建立例項*/
public static Singleton getInstance() {
/* if (instance == null) {
synchronized (instance){
instance = new Singleton();
}
}*/
return SingletonFactory.instance;
}
/*如果該物件被用於序列化 可以保證序列化前後一致*/
public Object readResolve() {
return SingletonFactory.instance;
}
}
4. 建造者模式 Builder
工廠類是提供建立單個類的模式 而建造者模式則是將各種產品集中起來進行管理 用來建立符合物件
舉例
介面Sender和實現類SmsSender MailSender與前面相同
建立Builder類
public class Builder {
List<Sender> senderList = new ArrayList<>();
public void produceSms(int count) {
for (int i = 0; i < count; i++) {
senderList.add(new SmsSender());
}
}
public void produceMail(int count) {
for (int i = 0; i < count; i++) {
senderList.add(new MailSender());
}
}
public List<Sender> getSenderList() {
return senderList;
}
}
5.原型模式 Prototype
------將一個物件作為原型 對其進行復制 克隆 產生一個和原物件類似的新物件
舉例:
public class Prototype implements Cloneable,Serializable {
private String string;
private SerializableObject obj;
/*淺複製*/
/*將一個物件複製後,基本資料型別的變數都會重新建立,而引用型別,指向的還是原物件所指向的。*/
public Object clone() throws CloneNotSupportedException {
Prototype prototype = (Prototype) super.clone();
return prototype;
}
/*深複製*/
/*將一個物件複製後,不論是基本資料型別還有引用型別,都是重新建立的*/
public Object deepClone() throws IOException, ClassNotFoundException {
/*寫入當前物件的二進位制流*/
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
/*獨處二進位制流產生的新物件*/
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
public SerializableObject getObj() {
return obj;
}
public void setObj(SerializableObject obj) {
this.obj = obj;
}
}
class SerializableObject implements Serializable{
}
測試類
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Prototype prototype = new Prototype();
prototype.setString("heqianqian");
prototype.setObj(new SerializableObject());
/*淺複製*/
Prototype p2 = (Prototype) prototype.clone();
System.out.println(p2.getString());
System.out.println(p2.getString()==prototype.getString());
System.out.println(p2.getObj()==prototype.getObj());
/*深複製*/
Prototype p3 = (Prototype) prototype.deepClone();
System.out.println(p3.getString());
System.out.println(p3.getString()==prototype.getString());
System.out.println(p3.getObj()==prototype.getObj());
}
}