1、什麼是設計模式?設計模式有什麼用?
設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理地運用設計模式可以完美地解決很多問題,每種模式在現實中都有相應的原理來與之對應,每種模式都描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案。
2、設計模式應該遵循的面向對象設計原則
1994年,在由設計模式四人幫GOF出版的著作Design Patterns - Elements of Reusable Object-Oriented Software(中文譯名:設計模式 - 可複用的面向對象軟件元素)中提出,設計模式應該遵循以下兩條面向對象設計原則:
- 對接口編程而不是對實現編程
- 優先使用對象組合而不是繼承
“對接口編程而不是對實現編程”,對於這句話我的理解就是:要善於使用多態。變量的聲明儘量使用超類型(父類),而不是某個具體的子類,超類型中的各個具體方法的實現都是寫在不同的子類中。程序在執行時能夠根據不同的情況來調用到不同的子類方法,這樣做更加靈活,並且我們在聲明一個變量時無需關心以後執行時的真正的數據類型是哪種(某個子類類型),這是種解耦合(鬆耦合)的思想。實例代碼如下:
package Test;
public interface Animal {
public void makenoise();
}
package Test;
public class Dog implements Animal {
@Override
public void makenoise() {
System.out.println("汪汪汪!");
}
}
class Cat implements Animal{
@Override
public void makenoise() {
System.out.println("喵喵喵!");
package Test;
public class AnimalTest {
public static void hearnoise(Animal animal){
animal.makenoise();
}
public static void main(String[] args) {
AnimalTest.hearnoise(new Dog());
AnimalTest.hearnoise(new Cat());
}
}
執行結果:
汪汪汪!
喵喵喵!
3、設計模式的六大原則
- 開閉原則(Open Close Prinprinciple),開閉原則的意思是:對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。簡言之,是爲了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類。
- 里氏代換原則(Liskov Substitution Principle),里氏代換原則是面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟件單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行爲。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範(LSP我曾經在另一篇文章重新思考接口和抽象類中舉了一個例子)。
- 依賴倒轉原則(Dependence Inversion Principle),這個原則是開閉原則的基礎,具體內容:針對接口編程,依賴於抽象而不依賴於具體。
- 接口隔離原則(Interface Segregation Principle),使用多個隔離的接口,比使用單個接口要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟件架構出發、便於升級和維護的軟件設計思想,它強調降低依賴,降低耦合。
- 迪米特法則,又稱最少知道原則(Demeter Principle),一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模塊相對獨立。
- 合成複用原則(Composite Reuse Principle),儘量使用合成/聚合的方式,而不是使用繼承。
4、設計模式的四種類型(包括J2EE設計模式)
創建型模式,這些設計模式提供了一種在創建對象的同時隱藏創建邏輯的方式,而不是使用 new 運算符直接實例化對象。這使得程序在判斷針對某個給定實例需要創建哪些對象時更加靈活。創建者模式包括以下幾種設計模式:
- 工廠模式
- 抽象工廠模式
- 單例模式
- 建造者模式
- 原型模式
結構型模式,這些設計模式關注類和對象的組合。繼承的概念被用來組合接口和定義組合對象獲得新功能的方式。結果型模式包括以下幾種設計模式:
- 適配器模式
- 橋接模式
- 過濾器模式
- 組合模式
- 裝飾器模式
- 外觀模式
- 享元模式
- 代理模式
行爲型模式,這些設計模式特別關注對象之間的通信。行爲型模式包括以下幾種設計模式:
- 責任鏈模式
- 命令模式
- 解釋器模式
- 迭代器模式
- 中介者模式
- 備忘錄模式
- 觀察者模式
- 狀態模式
- 空對象模式
- 策略模式
- 模板模式
- 訪問者模式
J2EE模式,這些設計模式特別關注表現層,這些模式是由Sun Java Center鑑定的。J2EE模式包括以下幾種設計模式:
- MVC模式
- 業務代表模式
- 組合實體模式
- 數據訪問對象模式
- 前端控制器模式
- 攔截過濾器模式
- 服務器定位器模式
- 傳輸對象模式
5、幾種常見的設計模式
5.1、工廠模式
工廠模式(Factory Pattern)是Java中最常見的設計模式之一,屬於創建者模式。顧名思義,它的思路是設計一個對象生產工廠,它提供了一種絕佳的創建對象的方式。在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過一個共同的接口來創建對象。
主要解決的問題:解決接口選擇的問題。定義一個創建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創建過程延遲到子類進行。
優點:
- 一個調用者想創建一個實例對象,只需要知道其名字就行。
- 擴展性高,如果想增加一個產品,只要擴展工廠類就行。
- 屏蔽了產品的具體實現,調用者只關心產品的接口。
缺點:
- 每次增加一個產品,都需要增加一個具體實現類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜性,同時也增加了系統具體類的依賴,這並不是什麼好事。
實現示例:就以一家生產多個不同品牌的汽車生產廠爲例,先創建一個Car接口和三個實現類BENZ、BMW、TOYOTA,再定義一個工廠類CarFactory。我們使用這個工廠類CarFactory來生產不同品牌的汽車。
package FactoryDemo;
public interface Car {
public void Brand();
}
public class BENZ implements Car{
@Override
public void Brand() {
System.out.println("生產一輛奔馳");
}
}
public class BMW implements Car{
@Override
public void Brand() {
System.out.println("生產一輛寶馬");
}
}
public class TOYOTA implements Car{
@Override
public void Brand() {
System.out.println("生產一輛豐田");
}
}
//用來生成汽車的工廠類
//equalsIgnoreCase()方法只能比較字符串,equals()可以比較字符串和對象,且equalsIgnoreCase()
//中不區別大小寫,A-Z和a-z是一樣的
public class CarFactory {
public Car getcar(String carbrand) {
if (carbrand.equalsIgnoreCase("BENZ")) {
return new BENZ();
} else if (carbrand.equalsIgnoreCase("BMW")) {
return new BMW();
} else if (carbrand.equalsIgnoreCase("TOYOTA")) {
return new TOYOTA();
} else
System.out.println("對不起我們不生產這輛車");
return null;
}
}
//實例化工廠類
public class CarFactoryTest {
public static void main(String[] args) {
CarFactory carfactory = new CarFactory();
Car car1 = carfactory.getcar("Benz");
car1.Brand();
Car car2 = carfactory.getcar("Bmw");
car2.Brand();
Car car3 = carfactory.getcar("toyota");
car3.Brand();
}
}
執行結果:
生產一輛奔馳
生產一輛寶馬
生產一輛豐田
5.2、抽象工廠模式
抽象工廠模式是圍繞一個超級工廠創建其他工廠,這個超級工廠是生產其他工廠的工廠。這種設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。在抽象工廠模式中,接口是負責創建一個相關對象的工廠,不需要顯式地指定它們的類。每個生成的工廠都能按照工廠模式提供對象。
主要解決的問題:主要解決接口選擇的問題。系統的產品有多於一個的產品族,而系統只消費其中某一族的產品。
優點:當一個產品族中的多個對象被設計在一起工作時,它能保證客戶端始終只使用一個產品族中的對象。
缺點:產品族的擴展非常困難,要增加一個系列的某個產品,不但要在創造工廠裏新增大量代碼,還要在具體實現里加代碼,產品族難以擴展。
實現示例:還是以上一個實例爲基礎來說明抽象工廠模式。一家汽車集團公司,旗下有兩個工廠,一家是生產國外品牌車(BENZ、BMW、TOYATA),另一家是生產國內品牌車(JAC、BYD、ROEWE)(這就相當於兩個產品族)。集團有一家自營銷售門店,採取顧客下單後再生產的經營策略(這就相當於系統只在一個時刻消費某一族的產品)。有兩個接口:ForeignCar(國外品牌車)、DomesticCar(國內品牌車),汽車實現類:Benz、Bmw、Toyota、Jac、Byd、Roewe。一個工廠抽象類AbstractFactory,兩個工廠類繼承自這個抽象類:ForeigncarFactory、DomesticcarFactory。最後還有一個工廠生產者FactoryProducer和一個測試類AbstractFactoryTest。具體代碼實現如下:
國外品牌車接口及實現類
public interface ForeignCar {
public void Brand();
}
public class BNEZ implements ForeignCar{
@Override
public void Brand() {
System.out.println("生產一輛奔馳");
}
}
public class BMW implements ForeignCar{
@Override
public void Brand() {
System.out.println("生產一輛寶馬");
}
}
public class TOYOTA implements ForeignCar{
@Override
public void Brand() {
System.out.println("生產一輛豐田");
}
}
國內品牌車接口及實現類
public interface DomesticCar {
public void Brand();
}
public class BYD implements DomesticCar{
@Override
public void Brand() {
System.out.println("生產一輛比亞迪");
}
}
public class JAC implements DomesticCar{
@Override
public void Brand() {
System.out.println("生產一輛江淮");
}
}
public class ROEWE implements DomesticCar{
@Override
public void Brand() {
System.out.println("生產一輛榮威");
}
}
工廠的抽象類及兩個工廠實現類
public abstract class AbstractFactory {
public abstract ForeignCar getforeigncar(String brand);
public abstract DomesticCar getdomesticcar(String brand);
}
public class ForeigincarFactory extends AbstractFactory{
@Override
public ForeignCar getforeigncar(String brand) {
if (brand.equalsIgnoreCase("Benz")){
return new BNEZ();
}
else if (brand.equalsIgnoreCase("Bmw")){
return new BMW();
}
else if (brand.equalsIgnoreCase("Toyota")){
return new TOYOTA();
}
else
System.out.println("我們沒有這個品牌授權");
return null;
}
@Override
public DomesticCar getdomesticcar(String brand) {
return null;
}
}
public class DomesticcarFactory extends AbstractFactory{
@Override
public ForeignCar getforeigncar(String brand) {
return null;
}
@Override
public DomesticCar getdomesticcar(String brand) {
if (brand.equalsIgnoreCase("Jac")){
return new JAC();
}
else if (brand.equalsIgnoreCase("Byd")){
return new BYD();
}
else if (brand.equalsIgnoreCase("Roewe")){
return new ROEWE();
}
else
System.out.println("我們沒有這個品牌授權");
return null;
}
}
工廠生產者及測試類
public class FactoryProducer {
public static AbstractFactory CreateFactory(String choice){
if (choice.equalsIgnoreCase("ForeignCar")){
return new ForeigincarFactory();
}
else if (choice.equalsIgnoreCase("DomesticCar")){
return new DomesticcarFactory();
}
else
System.out.println("我們沒有這個工廠");
return null;
}
}
public class AbstractFactoryTest {
public static void main(String[] args) {
AbstractFactory abstractFactory1 = FactoryProducer.CreateFactory("ForeignCar");
ForeignCar car1 = abstractFactory1.getforeigncar("toyota");
car1.Brand();
AbstractFactory abstractFactory2 = FactoryProducer.CreateFactory("DomesticCar");
DomesticCar car2 = abstractFactory2.getdomesticcar("byd");
car2.Brand();
}
}
執行結果
生產一輛豐田
生產一輛比亞迪
---------------------------------------------------------------------------待更新-------------------------------------------------------------------------------------