設計模式是爲了讓程序有更好的:java
對類來講,即一個類只負責一項職責。如類A負責兩個不一樣職責:職責1和職責2。當職責1需求變動而改變A時,也有可能致使職責2執行錯誤,所以須要把類A的粒度分解爲A1與A2。android
客戶端不該該依賴它不須要的接口,即一個類對另外一個類的依賴應該創建在最小的接口上。接口儘可能細化,同時接口中的方法儘可能少。ios
經過抽象(接口或者抽象類)使各個類或模塊的實現彼此獨立,不相互影響,實現模塊間的鬆耦合。程序員
面向接口編程算法
父類能出現的地方子類就能夠出現,並且替換成子類也不會出現任何錯誤或者異常,而使用者也無需知道父類仍是子類。數據庫
軟件實體(包括類、模塊、功能等)應該對擴展開放,可是對修改關閉。編程
UML--Unified modeling language(統一建模語言),是一種用於軟件系統分析和設計的語言工具,它用於幫助軟件開發人員進行思考和思路的結果。 ####UML類圖分類設計模式
所謂類的單例設計模式,就是採起必定的方法保證在整個的軟件系統中,對某個類只能存在一個對象實例,而且該類只提供一個取得其對象實例的方法(靜態方法)。安全
class Singleton {
private Singleton() {
}
private static final Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
複製代碼
這種寫法比較簡單,就是在類裝載的時候就完成實例化,避免了線程同步的問題。多線程
在類加載的時候就完成了實例化,沒有達到懶加載的效果。若是從始至終都沒有用過這個實例,則會形成內存的浪費。
這種單例設計模式可用,可是可能會出現內存浪費。
class Singleton {
private Singleton() {
}
private static Singleton singleton;
static {
singleton = new Singleton();
}
public static Singleton getInstance() {
return singleton;
}
}
複製代碼
與餓漢式(靜態常量)的方式相似,只不過將類實例化的過程放在了靜態代碼塊中,優缺點與餓漢式(靜態常量)同樣。
class Singleton {
private Singleton() {
}
private static Singleton singleton;
/** * 當調用的時候再建立對象,可是線程不安全。好比線程1執行到singleton爲空,此時線程2也執行到此爲止,而後就會產生兩個對象。 * @return */
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
複製代碼
if (singleton == null)
判斷語句塊,還將來得及往下執行,另外一個線程也經過了這個判斷語句,這是便會產生多個實例。所以在多線程的環境下不可使用這個方式。class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
複製代碼
class Singleton {
private Singleton() {
}
//爲了不初始化操做的指令重排序
private static volatile Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
複製代碼
class Singleton {
private Singleton() {
}
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
複製代碼
enum Singleton{
INSTANCE;
}
複製代碼
java.lang.Runtime就是經典的單例模式(餓漢式),代碼以下:
private static Runtime currentRuntime = new Runtime();
/** * Returns the runtime object associated with the current Java application. * Most of the methods of class <code>Runtime</code> are instance * methods and must be invoked with respect to the current runtime object. * * @return the <code>Runtime</code> object associated with the current * Java application. */
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
複製代碼
經過舉個例子說明一下: 我喜歡養寵物,抽象一個寵物父類或者接口。
/** * 描述要養的寵物 * * @author bf * @date 2019/9/18 17:01 */
public interface Animal {
void getAnimal();
}
複製代碼
先養一個小狗吧:
public class Dog implements Animal {
@Override
public void getAnimal() {
System.out.println("養了一隻小狗");
}
}
複製代碼
再養一隻小貓:
public class Cat implements Animal {
@Override
public void getAnimal() {
System.out.println("養了一隻小貓");
}
}
複製代碼
再養一隻竹鼠:
public class Mouse implements Animal {
@Override
public void getAnimal() {
System.out.println("養一隻竹鼠");
}
}
複製代碼
準備工做完成了,咱們去寵物館(簡單工廠類),寵物種類以下:
public class SimpleAnimalFactory {
private static final int TYPE_DOG = 1;
private static final int TYPE_CAT = 2;
private static final int TYPE_MOUSE = 3;
public static Animal createAnimals(int type) {
switch (type) {
case TYPE_DOG:
return new Dog();
case TYPE_CAT:
return new Cat();
case TYPE_MOUSE:
return new Mouse();
default:
return null;
}
}
}
複製代碼
簡單寵物館就提供三種動物,你說要什麼,他就給你什麼。這裏我要了一隻狗:
public static void main(String[] args) {
Animal animals = SimpleAnimalFactory.createAnimals(1);
animals.getAnimal();
}
複製代碼
輸出以下:
養了一隻小狗
使用反射實現簡單工廠:
public static <T> T createAnimals(Class<T> clz) throws Exception {
T result = null;
result = (T) Class.forName(clz.getName()).newInstance();
return result;
}
複製代碼
買寵物時調用:
Dog dog = SimpleReflectAnimalFactory.createAnimals(Dog.class);
dog.getAnimal();
複製代碼
工廠方法模式是簡單工廠的進一步深化,在工廠方法模式中,咱們再也不提供一個統一的工廠類來建立全部的對象,而是針對不一樣的對象提供不一樣的工廠。也就是說每個對象都有一個與之對應的工廠。
定義一個用於建立對象的接口,讓子類決定實例化哪個類。工廠方法使一個類的實例化延遲到其子類。
咱們能夠先經過實例來詳細解釋一下這個定義:
依然使用養寵物的例子: 首先,編寫一個寵物接口:
public interface Animal {
void getAnimal();
}
複製代碼
養狗的代碼:
public class Dog implements Animal {
@Override
public void getAnimal() {
System.out.println("養了一隻小狗");
}
}
複製代碼
養貓的代碼:
public class Cat implements Animal {
@Override
public void getAnimal() {
System.out.println("養了一隻小貓");
}
}
複製代碼
養竹鼠的代碼:
public class Mouse implements Animal {
@Override
public void getAnimal() {
System.out.println("養一隻竹鼠");
}
}
複製代碼
如今咱們按照定義所說定義一個抽象的工廠接口GetAnimalFactory
public interface GetAnimalFactory {
Animal getAnimal();
}
複製代碼
Dog加載器工廠
public interface GetAnimalFactory {
Animal getAnimal();
}
複製代碼
Cat加載器工廠
public class GetCat implements GetAnimalFactory {
@Override
public Animal getAnimal() {
return new Cat();
}
}
複製代碼
Mouse加載器工廠
public class GetMouse implements GetAnimalFactory {
@Override
public Animal getAnimal() {
return new Mouse();
}
}
複製代碼
和簡單工廠對比一下,最根本的區別在於簡單工廠只有一個統一的工廠類,而工廠方法是針對每一個要建立的對象都會提供一個工廠類。
提供一個建立一系列相關或相互依賴對象的接口,而無需指定它們具體的類。(在抽象工廠模式中,每個具體工廠都提供了多個工廠方法用於產生多種不一樣類型的對象) 抽象工廠能夠劃 分爲4大部分:
如今須要作一款跨平臺的遊戲,須要兼容Android和Ios兩個移動操做系統,該遊戲針對每一個系統都設計了一套操做控制器(OperationController)和界面控制器(UIController),下面經過抽象工廠方式完成這款遊戲的架構設計。 先建立兩個抽象產品接口。
抽象操做控制器
public interface OperationController {
void control();
}
複製代碼
抽象界面控制器
public interface UIController {
void display();
}
複製代碼
而後完成這兩個系統平臺的具體操做控制器和界面控制器
Android平臺
public class AndroidOperationController implements OperationController {
@Override
public void control() {
System.out.println("AndroidOperationController");
}
}
複製代碼
public class AndroidUIController implements UIController {
@Override
public void display() {
System.out.println("AndroidUIController");
}
}
複製代碼
Ios
public class IosOperationController implements OperationController {
@Override
public void control() {
System.out.println("IosOperationController");
}
}
複製代碼
public class IosUIController implements UIController {
@Override
public void display() {
System.out.println("IosUIController");
}
}
複製代碼
下面定義一個抽象工廠,該工廠能夠建立OperationController和UIController
public interface SystemFactory {
OperationController createOperationController();
UIController createUIController();
}
複製代碼
而後在各平臺具體的工廠類中完成操做控制器和界面控制器的建立過程
Android
public class AndroidFactory implements SystemFactory {
@Override
public OperationController createOperationController() {
return new AndroidOperationController();
}
@Override
public UIController createUIController() {
return new AndroidUIController();
}
}
複製代碼
Ios
public class IOSFactory implements SystemFactory {
@Override
public OperationController createOperationController() {
return new IosOperationController();
}
@Override
public UIController createUIController() {
return new IosUIController();
}
}
複製代碼
方法調用:
public static void main(String[] args) {
//android
AndroidFactory androidFactory = new AndroidFactory();
OperationController androidOperationController = androidFactory.createOperationController();
UIController androidUiController = androidFactory.createUIController();
androidOperationController.control();
androidUiController.display();
//ios
IOSFactory iosFactory = new IOSFactory();
OperationController iosFactoryOperation = iosFactory.createOperationController();
UIController iosUiController = iosFactory.createUIController();
iosFactoryOperation.control();
iosUiController.display();
}
複製代碼
針對不一樣平臺只經過建立不一樣的工廠對象就完成了操做和UI控制器的建立。
定義一個操做中的算法的框架,而將一些步驟延遲到子類中。使得子類能夠不改變一個算法的結構便可從新定義該算法的某些特定步驟。
舉個騎共享單車的例子,無論是騎哈羅單車仍是騎摩拜單車,咱們都要經歷掃碼解鎖、騎車、上鎖、支付這四個過程,所以能夠設計成一個抽象方法。
public abstract class RideBike {
/** * 掃碼解鎖 */
public abstract void unlock();
/** * 騎車 */
public abstract void ride();
/** * 上鎖 */
public abstract void lock();
/** * 支付 */
public abstract void pay();
/** * 模擬使用共享單車 */
public abstract void use();
}
複製代碼
上班去了,開始去騎共享單車,發現了一輛哈羅單車,開始使用。
public class RideHelloBike extends RideBike {
@Override
public void unlock() {
System.out.println("掃碼解鎖哈羅單車");
}
@Override
public void ride() {
System.out.println("開啓騎哈羅單車");
}
@Override
public void lock() {
System.out.println("給哈羅單車上鎖");
}
@Override
public void pay() {
System.out.println("支付哈羅單車的費用");
}
@Override
public void use() {
this.unlock();
this.ride();
this.ride();
this.pay();
}
}
複製代碼
終於下班了,發現公司樓下的哈羅單車都被騎完了,那就騎摩拜單車吧。
public class RideMoBaiBike extends RideBike {
@Override
public void unlock() {
System.out.println("掃碼解鎖摩拜單車");
}
@Override
public void ride() {
System.out.println("開啓騎摩拜單車");
}
@Override
public void lock() {
System.out.println("給摩拜單車上鎖");
}
@Override
public void pay() {
System.out.println("支付摩拜單車的費用");
}
@Override
public void use() {
this.unlock();
this.ride();
this.ride();
this.pay();
}
}
複製代碼
騎摩拜和騎哈羅都用到了一樣的方法,user()方法,代碼重複了,這是病得治,藥房就是使用模板方法模式。
模板方法模式相信你們都用過,就是抽象類裏面的方法,不須要改變的方法。那麼,下面咱們開始編寫代碼。
抽象類代碼
public abstract class AbstractClass {
protected abstract void unlock();
protected abstract void ride();
protected abstract void lock();
protected abstract void pay();
protected final void use() {
this.unlock();
this.ride();
this.lock();
this.pay();
}
}
複製代碼
這裏說明一下:使用protected,只有同包下的父子類能夠訪問。 final修飾的方法代表子類不能重寫父類方法。
實現類代碼
public class ScanBicycle extends AbstractClass {
@Override
protected void unlock() {
System.out.println("掃碼解鎖");
}
@Override
protected void ride() {
System.out.println("騎車");
}
@Override
protected void lock() {
System.out.println("上鎖");
}
@Override
protected void pay() {
System.out.println("支付");
}
public static void main(String[] args) {
ScanBicycle bicycle = new ScanBicycle();
bicycle.use();
}
}
複製代碼
運行結果以下:
public class ScanBicycle extends AbstractClass {
@Override
protected void unlock() {
System.out.println("掃碼解鎖哈羅單車");
}
@Override
protected void ride() {
System.out.println("騎哈羅車");
}
@Override
protected void lock() {
System.out.println("上哈羅鎖");
}
@Override
protected void pay() {
System.out.println("支付哈羅費用");
}
public static void main(String[] args) {
ScanBicycle bicycle = new ScanBicycle();
bicycle.use();
}
}
複製代碼
模板方法看上去簡單,可是整個模式涉及到的都是面向對象設計的核心,好比繼承封裝、基於繼承代碼的複用、方法實現等等。當中還有涉及到一些關鍵詞的使用,也是咱們在Java編程中須要掌握的基礎。