Android進階知識樹——23種設計模式詳解

關於設計模式很對開發者都知道很重要,但陸陸續續學習過很多次,但學過的設計模式也基本忘了差不多,能記住的還是之前使用的幾個基本的,現在藉此機會將23 中設計模式完整的梳理學習下,Java設計模式分類:

  • 創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。

  • 結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。

  • 行爲型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式

本篇所有的實例代碼和uml圖地址:JavaDesignModule

1、Factory Method模式
  • 簡介:用Template Method的模式來構建生成實例的工廠,這就是工廠方法模式;

Factory Method模式原理來自意見產品的生產過程,從定義上的理解,將某一類事物按照面向對象的思想抽取成產品類,然後創建對應的工廠按照固定的模式創建相應的產品,相當於工廠裏的產品生產線,一般使用工廠模式時都伴隨着相似功能的類,這裏的工廠模式也主要是根據條件創建正確的類;

  • 設計模式的意義
  1. 工廠模式實現了對所有產品創建的管理,將產品和具體使用產品的類解耦
  2. 利於程序的擴展,有新添加產品時,只需要分別實現對應的產品類和工廠類即可
  • 功能角色
    在這裏插入圖片描述
  1. Product:抽象的產品類,內部定義產品的功能屬性和方法;
  2. ObjectProduct:具體實現的產品類,針對具體的產品實現具體的功能;
  3. ProductFactory:抽象工廠類,定義創建產品的抽象方法;
  4. ObjectProductFactory:實現抽象工廠類,重寫抽象方法根據設定的條件創建具體的產品;
  • 使用
  1. 創建抽象產品類,並實現具體的產品和方法
interface Product { 
    fun action()
}
class Car(val name : String) : Product { // 具體的產品類
    override fun action() {
        Log.e("Car -------- ","$name 是私家車!自己駕駛!")
    }
}
  1. 創建產品工廠接口,並創建實現類
interface ProductFactory {
    fun createProduct(name: String):Product //創建Product抽象方法
}
open class CarFactory : ProductFactory { // 具體的工廠類
    override fun createProduct(name: String): Product {
        return Car(name)
    }
}
  1. 使用工廠模式
val car = CarFactory().createProduct("Audi") //創建具體工廠類,並調用方法創建產品
car.action()
  • 意義
    工廠模式利用抽象產品的轉型和統一管理產品的創建,將所有的實際產品和使用這些產品的代碼解耦,有利於代碼的擴展和維護

  • 使用場景

  1. 針對某個抽象產品有多種實現時,可以使用工廠方法分別實現不同的產品類,然後再工廠中按需求創建產品;
2、Abstract Factory模式
  • 簡介
    上面的工廠方法模式只是只針對單一類的產品,而對於多個產品時,如果繼續使用工廠模式則勢必要工廠類,這違背了開閉原則,此時需要使用抽象工廠模式,將工廠抽象出來,在工廠接口中定義多個產品的生產方法,然後讓每個具體的工廠分別生產自己對應的一個或多個產品,從而形成不同的工廠羣和各自產品線的形式;

  • 使用實例

  1. 定義兩個產品Computer和Phone
abstract class Computer{
    abstract fun action()
}

abstract class Phone(var name: String = "Phone") {
    abstract fun call(number : String)
}
  1. 目前分別有兩家工廠Green和Red生產,現在實現它們各自對應的產品類
class ComputerGreen : Computer() { // 電腦
    override fun action() {
        System.out.println(" Action by  Green Computer !")
    }
}
class PhoneGreen : Phone() { // 手機
    override fun call(number: String) {
        System.out.println(" Call $number  by  Green Phone !")
    }
}

class ComputerRed : Computer() {
    override fun action() {
        System.out.println(" Action by  Red Computer !")
    }
}
class PhoneRed : Phone() {
    override fun call(number: String) {
        System.out.println(" Call $number  by  Red Phone !")
    }
}
  1. 創建工廠接口,聲明創建兩個產品的方法
interface Factory {
    fun createComputer() : Computer//生產電腦
    fun createPhone() : Phone // 生產手機
}
  1. 實現兩個各自的工廠類,分別創建自己的產品
class FactoryGreen : Factory {
    override fun createComputer(): Computer {
        return ComputerGreen() //生產Green電腦
    }
    override fun createPhone(): Phone {
        return PhoneGreen() //生產Green手機
    }
}

class FactoryRed : Factory {
    override fun createComputer(): Computer {
        return ComputerRed() //生產Red電腦
    }
    override fun createPhone(): Phone {
        return PhoneRed() //生產Red手機
    }
}
  1. 使用抽象工廠
val factoryGreen = FactoryGreen() // 創建Green工廠
factoryGreen.createComputer()
factoryGreen.createComputer()

val factoryRed = FactoryRed() // 創建Red工廠
factoryRed.createComputer()
factoryRed.createComputer()
  • 意義:在工廠模式的基礎上將工廠也抽象出來,這樣可以實現多個工廠和多個產品的存在,當增加工廠個產品時直接創建其擴展類即可,無需修改原來的代碼
  • 使用場景:針對多種產品和多個分類時,可以使用抽象工廠模式統一管理所有產品
3、Singleton模式
  • 簡介
    單例模式是開發中經常使用的基本模式,尤其在一些工具類或框架的初始化中,如:Glide、EventBus等,它的最主要特性就是確保一般情況下程序中只有一個實例;

  • 功能角色
    在這裏插入圖片描述

  • 使用實例

  1. 雙重判斷方式提供單例
class Singleton private constructor() {
    companion object {
        var singleton: Singleton? = null
        fun getIntance(): Singleton {
            if (singleton == null) {
                synchronized(Singleton::class.java) {
                    singleton = Singleton()
                }
            }
            return singleton!!
        }
    }
}
  1. 靜態內部類方式
class SingletonInner private constructor() {
    class SingleHolder {
        companion object {
            val singletonInner = SingletonInner()
        }
    }
}
  • 使用單例模式
val single = Singleton.getIntance()
val singleInner = SingletonInner.SingleHolder.singletonInner
  • 意義:對於經常需要使用但每次需要獲取的對象,或初始化比較複雜的對象使用單例提供,節省獲取或初始化的時間;
  • 使用場景:想確保程序中只存在一個實例存在;
4、Builder模式
  • 簡介

Builder設計模式是很多人第一個接觸的設計模式,就基本的Dialog就是使用Builder完成初始化,它利用在創建實例前使用Builder設置和保存配置的方式,極大的豐富了對象的使用場景和配置,而且使用中形成了鏈式調用;

  • 功能角色
    在這裏插入圖片描述

  • 使用實例:參見AlertDialog的使用

  • 意義:簡化並統一對象的初始化過程,豐富對象的使用場景

  • 使用場景
    對於某個功能基本使用相同,但場景變化非常多時可以使用Builder模式,將具體和功能和變換的配置解耦;

5、Prototype模式
  • 簡介
    原型模式,從名稱的中就能大概讀出他的意思,它是利用已有的對象直接克隆出新對象,相當於本身的複製版,從而避免一系列的初始化和創建工作,並且可以保持和現有對象完全一樣的數據,對於原型本身需要具有調用clone()方法的能力,一般實現Cloneable接口即可;

  • 功能角色
    在這裏插入圖片描述

  1. Prototype:原型接口,繼承Cloneable接口
  2. PrototypeImpl:原型接口的實現類,也是克隆新對象的原始標本
  • 使用實例
  1. 創建類實現Cloneable接口,在具體的類中調用clone()方法克隆對象
open class BaseProduct : Cloneable {
    var name: String? = null
    var price = 0
}

class CarProduct : BaseProduct(){
    var coclor = "紅色"
    fun createClone(): CarProduct { // 提供克隆方法
        return super.clone() as CarProduct // 克隆並轉換對象
    }
}
  1. 對於對象嵌套的原型,即需要克隆的隊像中持有別的對象;
class FoodProduct(var food: Food) : BaseProduct(){
    fun createClone(): FoodProduct {
        val product = super.clone() as FoodProduct 
        product.food = food.createClone()// 內部存儲的對象也需要克隆並賦值
        return product
    }
}
  1. 上面代碼中的FoodProduct中保存了Food對象,在克隆時如果要確保對象數據一致,就必須讓food對象也克隆一份,所以Food對象也要實現Cloneable接口
open class Food(
    var price: Int = 0,
    var shop: String = "Shop"
) : Cloneable{ //因爲淺複製的原因,Food也要實現Cloneable並且提供複製方法
    fun createClone(): Food {
        return super.clone() as Food
    }
}
  1. 使用原型模式
val car = CarProduct()
car.name = "Audi"
car.price = 10000

val cerClone = car.createClone() // 克隆對象
Log.e("Car Name ---",cerClone.name)
Log.e("Car Price ---",cerClone.price.toString())
Log.e("Car Color ---",cerClone.coclor)
  • 意義:避免對象的創建,而且可以複製出一份和當前對象數據一致的副本
6、Adapter模式
  • 簡介

對於適配器我們接觸做多的就是電源適配器,對於我們身邊的電源一般都提供的是220V交流電,但不同的電器設備所需的電壓不同,那如何讓它們正常使用呢?這就需要適配器的存在,它可以將200V的電壓轉換爲各自需要的電壓,而且不同的國家之間提供的電源也不同,那麼經常出差的人員可能就會配備多種不同的適配器,但它們的作用只有一個將現有不可直接使用的轉換爲可用的;

同樣的道理也適應在程序中,如果給你一個寫好的工具類,需要將它使用在其他程序上,當然如果能直接使用最好,如果不能就需要在二者之間構建適配器

  • 功能角色
    在這裏插入圖片描述
  1. Target:創建抽象接口,在接口聲明目標要調用的方法
  2. Adaptee:本來存在的工具類,即想要適配後調用的類
  3. Adapter:適配器
  • 使用實例

假設目前只有一個輸出Int類型的工具類PrintInt,而提供的數據源是String類型,現在需要用適配器去讓PrintInt可以執行;

  1. PrintInt類
open class PrintInt {
   protected fun printInt(number : Int){
        Log.e("輸出數字","number = $number")
    }
}
  1. 聲明接口類和抽象方法
interface PrintString {
    fun print(message : String)// 聲明方法傳入字符串
}
  1. 創建適配器處理中間的數據轉換
class Adapter : PrintString ,PrintInt(){
    override fun print(message : String) { //重寫方法
        when(message){
            "123" -> {printInt(123)}//將輸入的字符串調用printInt()輸出
        }
    }
}
  1. 使用適配器
val adapter = Adapter()
adapter.print("123")
  • 意義:保證開閉原則的前提下,擴展了原有程序的使用場景,也解決了程序間不可直接使用的問題
  • 使用場景
  1. 當一個類希望轉換成滿足另一個類或接口時,就需要使用適配器模式,處理中間的數據轉換
  2. 對於適配器還有一種對象適配器,和類不同的是,內部採用持有對象的形式調用方法,無需繼承或實現類
7、Decorator模式
  • 簡介
    Decorator意思爲裝飾者模式,從字面上能理解大致意思,在原有類或對象的基礎上添加額外的功能,根據開閉原則原則不能修改原來的程序,此時裝飾者即可實現功能,裝飾者有兩個基本要素:
  1. 裝飾者和被裝飾者實現同一個接口,保證方法的調用一致
  2. 裝飾者內部持有被裝飾者的對象,最終需要調用被裝飾者的方法
  • 功能角色
    在這裏插入圖片描述
  • 使用實例

假設現在有一個功能是按照行和列輸出字符串,現在有個需求要在輸出的字符串上下左右添加邊框,在不改變原來程序的基礎上使用裝飾者模式實現

  1. 被包裝對象的抽象類
abstract class Component {
    abstract fun getRows(): Int // 獲取字符串行數
    abstract fun getColumns(): Int // 獲取字符串列數
    abstract fun getTextByRow(number: Int): String // 獲取對應的字符串
    fun show() { // 顯示內容
        val rows = getRows()
        for (index in 0 until rows) {
            System.out.println(getTextByRow(index))
        }
    }
}
  1. 被包裝對象實現類,實現被包裝抽象方法
class ConcerteComponent(val text: String) : Component() {
    override fun getRows() = 1
    override fun getColumns(): Int {
        return text.length
    }
    override fun getTextByRow(number: Int): String {
        return text
    }
}
  1. 創建包裝類的抽象類,內部持有被包裝對象
abstract class Decorator (val component: Component) : Component()
  1. 實現具體的包裝類
class ConcerteDecorator constructor(component: Component) : Decorator(component) {
    override fun getRows(): Int {
        return component.getRows() + 2
    }
    override fun getColumns(): Int {
        return component.getColumns() + 2
    }
    override fun getTextByRow(number: Int): String {
        return when (number) {
            0 -> {"----------"}
            getRows() - 1 -> {"----------"}
            else -> {
                "| ${component.getTextByRow(number)} |"
            }
        }
    }
}

從上面的包裝類看出,它繼承了同樣的抽象類實現了同樣的抽象接口,在重寫的方法中主體還是調用被包裝對象方法,然後在返回結果上增加處理;

  1. 使用裝飾者模式
val component = ConcerteComponent("HELLO")
val concerteDecorator = ConcerteDecorator(component)
concerteDecorator.show()
  • 意義:更容易擴展原有程序的功能,而無需修改或影響原有程序

  • 使用場景:當需要拓展某個對象功能時,且包裝對象和被包裝對象具有一致性時使用裝飾着模式進行擴展

8、Proxy模式
  • 簡介

代理模式是使用一個相似的對象來替代真實的對象,從而實現一系列的功能修改,好比現實中的中介代理一樣,當你需要某種需求時直接找代理,它們會幫你完成功能的實現;

  • 功能角色
    在這裏插入圖片描述
  • 使用實例
  1. 現有的接口和功能如下
interface PrintInterface { // 接口方法
    fun setName(name: String)
    fun getName(): String
    fun print(content: String)
}
// 原有實現的功能方法
class RealPrint(private var nameReal: String) : PrintInterface {
    override fun setName(name: String) {
        action()
        nameReal = name
    }
    override fun getName(): String {
        action()
        return nameReal
    }
    override fun print(content: String) {
        Log.e("RealPrint = ", content)
    }
    private fun action(){}
}

現在已經有一個接口和對應實現的工具類RealPrint,但在RealPrint中的set和get兩個方法中每次都會調用額外的action()方法,現在的需求是要使用此類但又不要做額外的工作,此時使用代理模式修改set和get方法

  1. 設置代理類
class ProxyPrint( private var nameProxy: String) : PrintInterface {
   private var realPrint: RealPrint? = null
    override fun setName(name: String) {
        realPrint?.setName(name)
        nameProxy = name
    }
    override fun getName(): String {
        return nameProxy
    }
    override fun print(content: String) {
        initRealPrint()
        realPrint?.print(content)
    }
    private fun initRealPrint() {
        if (realPrint == null) {
            realPrint = RealPrint(nameProxy)
        }
    }
}

在代理類ProxyPrint同樣繼承方法接口,而且內部保存着真實對象,在代理中修改了setName()和getName()方法,刪除調用的action()對象,在print()中因爲要用到代理類的方法,所以創建代理類對象;

  • 意義:可以根據具體的需求實現對原有程序的部分邏輯進行修改,而不影響整體使用
  • 使用場景

當某個工具類或方法需要修改時,爲了不違反開閉原則可以使用代理模式,將方法的修改部分和原始部分分離,互不影響;

9、Facade模式
  • 簡介

外觀模式主要的功能解決類之間的依賴關係,以面向對象的思想編程,會將某個對象的功能方法封裝在一起,但又是一個程序或模塊的執行需要依賴相關聯的介個類,那爲了不讓類之間出現耦合就可採用外觀模式處理類的關係,從而實現類的解耦;

  • 功能角色
    在這裏插入圖片描述
  • 使用實例
    以汽車爲例,當汽車啓動時發動機和儀表盤等都會啓動,然而無需讓用戶一個個去啓動,更不用讓發動即啓動後去控制儀表盤,此時我們只需採用外觀模式將所有的啓動封裝好,對用戶來說就是一鍵啓動即可;
  1. 創建發動機、儀表盤各自的啓動和停止功能
class Dashboard {
    fun start() {
        System.out.println("儀表盤啓動!")
    }
    fun stop(){
        System.out.println("儀表盤停止!")
    }
}

class Engine {
    fun start() {
        System.out.println("發動機啓動!")
    }
    fun stop(){
        System.out.println("發動機停止!")
    }
}
  1. 創建Car類統一管理發動機、儀表盤的啓動
class Car() {
    private var dashboard: Dashboard = Dashboard()
    private var engine: Engine = Engine()
    fun start() {
        engine.start()
        dashboard.start()
    }
    fun stop() {
        engine.stop()
        dashboard.stop()
    }
}
  1. 使用
val car = Car()
car.start()
car.stop()
  • 意義:外觀模式將幾個關係類組合在一起,避免了類之間的相互引用實現代碼解耦,同時將固定的邏輯封裝後對外提供,簡化了使用過程;
  • 使用場景:
  1. 對於某個功能模塊需要多個小的程序按照一定的邏輯執行時,此時就可已使用外觀模式封裝;
  2. 需要對外提供更高級的API調用,而隱藏一些細節實現或API時,採用外觀模式封裝使用戶一鍵執行即可
10、Bridge模式
  • 簡介
    橋接模式就是把使用和其具體實現分開,使他們可以各自獨立的變化。橋接的用意是:將抽象化與實現化解耦,使得二者可以獨立變化,實際是將代碼的功能層次結構和實現層次結構相分離(這兩個概念後面會介紹)

  • 功能角色
    在這裏插入圖片描述

  1. 功能層次結構:上圖中Function類中有兩個方法,如果此時需要添加方法,可以創建子類AddFunction並在子類中添加方法,這種以創建子類達到添加功能的結構叫做功能層次結構
  2. 實現層次結構:上圖中在接口Implementer中聲明抽象方法,然後在ImplementerImpl類中實現功能,這種結構叫做實現層次結構
  • 使用實例
  1. 聲明接口方法,並實現具體功能
interface Implementer {
    fun action(msg: String)
    fun print(msg: String)
}

class PrintImplementer : Implementer{
    override fun action(msg: String) {
        System.out.println(" Print Action = $msg")
    }
    override fun print(msg: String) {
        System.out.println(" Print Print= $msg")
    }
}
  1. 創建對外使用的類,內部橋接真實的實現類
    val implementer: Implementer = PrintImplementer() // 內部創建對象
    fun action(msg: String) {
        implementer.action(msg) // 調用真實實現的方法
    }
    fun print(msg: String) {
        implementer.print(msg)
    }
}
  1. 實現橋接模式的功能層次結構擴展方法
class AddFunction:Function() {
    fun doSomething(msg: String) { // 外觀模式
        action(msg)
        print(msg)
    }
}
  • 意義:通過橋接模式將程序中的使用和具體的實現分離解耦,方便程序的維護
  • 使用場景:需要將功能層次和實現層次分離,從而兩者都需要擴展時使用
11、Composite模式
  • 簡介
    組合模式定義:能夠使容器和內容具有一致性,創造出遞歸結構的模式,可能定義比較抽象,它主要想形容一種數據結構,你可把它想像成文件和文件夾一樣,將文件夾當成容器,文件夾裏面即可以放文件也可以放文件夾,此時就是容器和內容一致;

  • 功能角色
    在這裏插入圖片描述

  • 使用實例:以文件夾結構爲例

  1. 創建基礎數據類
abstract class Entry {
    var parent: Entry? = null // 父文件夾
    var name: String? = null //文件名稱

    abstract fun getSize(): Int  // 文件大小
    abstract fun getAbslouteName() : String? // 文件路徑
    abstract fun printStrign( msg: String) // 輸出信息
}
  1. 創建文件夾(容器類)
class Directory(val nameFile: String, val parentFile: Entry? = null) : Entry() {
    private val arrayList = ArrayList<Entry>() // 保存容器內部文件
    init {
        name = nameFile
        parent = parentFile
    }
    override fun getSize(): Int { // 遍歷獲取文件夾大小
        val iterator = arrayList.iterator()
        var size = 0
        while (iterator.hasNext()){
            size += iterator.next().getSize()
        }
        return size
    }

    override fun printStrign(msg: String) {
        val iterator = arrayList.iterator()
        while (iterator.hasNext()){
            iterator.next().printStrign("$msg / $name")
        }
    }

    override fun getAbslouteName(): String? { 
        if (parentFile == null) {
            return name
        }
        return parentFile.getAbslouteName() +"/"+ name
    }

    fun add(entry: Entry) { // 向文件夾內添加文件
        entry.parent = this
        arrayList.add(entry)
    }
}
  1. 創建文件類
class File(val nameFile: String, val sizeFile: Int = 0, var parentFile: Entry? = null) : Entry() {
    override fun printStrign(msg: String) {
        Log.e("======","$msg / $name")
    }
    override fun getAbslouteName(): String? {
        if (parent == null) {
            return name
        }
        return parent!!.getAbslouteName() +"/"+ name
    }
    init {
        name = nameFile
        parent = parentFile
    }
    override fun getSize(): Int = sizeFile
}
  1. 使用實例
val rootD = Directory("root") // 創建跟目錄
        val bin = Directory("bin",rootD) // 創建二級文件夾
        val tem = Directory("tem",rootD)
        val user = Directory("user",rootD)

        val vi = File("vi",1000) // 創建文件
        val latex = File("latex",500)
        val tem_in = File("latex",800)

        rootD.add(bin) // 添加文件
        rootD.add(tem)
        rootD.add(user)
        bin.add(vi)
        bin.add(latex)
        tem.add(tem_in)
        bin.add(tem)
  • 意義:按照數據和容器某種聯繫,將所有的對象組合成一個整體,便於程序的管理
  • 使用場景:當容器和內容具有某種一致性時,使用組合模式管理對象
12、Flyweight模式
  • 簡介
    享元模式:主要是利用緩存對象的方式實現對象的共享,從而減少對象的創建,在開發中也很常見,通常與Factory一起使用,通過工廠產生數據前會線查看緩存信息,如果有則直接返回否則創建新對象並緩存;

  • 功能角色
    在這裏插入圖片描述

  • 使用實例:JDBC連接池

  • 意義:避免了對象的多次創建,提高程序的整體性能

  • 使用場景:每次使用都需要創建對象時,避免多次創建可以使用Flyweight模式

13、Iterator模式
  • 簡介
    最早接觸迭代器模式應該是在集合部分,當需要遍歷Map等集合時使用迭代器,從迭代器作用知道迭代器功能有兩點:
  1. 判斷是否有更多數據
  2. 獲取下一條數據
  • 功能角色
    在這裏插入圖片描述
  • 使用實例
  1. 聲明迭代器接口和集合接口
interface Iterator {
    /**
     * 判斷是否有下一條數據
     */
    fun hasNext(): Boolean
    /**
     * 根據下標遍歷獲取數據
     */
    fun next() : Any
}

interface Congregation {
    /**
     * 初始化迭代器
     */
 fun createIterator():Iterator
}
  1. 創建迭代器的實現類,內部持有集合類
class StudentIterator(private val congregation: StudentCongregation) : Iterator {
    var index = 0
    override fun hasNext(): Boolean {
        return index < congregation.getSize()
    }
    override fun next(): Student {
        val student = congregation.get(index)
        index++
        return student
    }
}
  1. 創建集合的實現類,重寫方法返回迭代器的實例
class StudentCongregation : Congregation {
   private val list by lazy { arrayListOf<Student>()}
    override fun createIterator(): StudentIterator { // 創建具體的迭代器
        return StudentIterator(this)
    }
    fun getSize():Int{
        return list.size
    }
    fun add(student: Student){
        list.add(student)
    }
    fun get(index : Int): Student{
        return list[index]
    }
}
  1. 使用迭代器
val congregation = StudentCongregation()
        for (id in 0..5) {
         congregation.add(Student(id,"name = id",id * 10))
        }
        val iterator = congregation.createIterator()
        while (iterator.hasNext()){
            val student = iterator.next()
            Log.e("=====","id = ${student.id} name = ${student.name} age = ${student.age}")
        }
  • 意義:對一些自定義保存數據的類提供一個統一的遍歷查詢的方法
  • 使用場景:需要保存和遍歷大量對象時,使用迭代器分離保存和查詢操作
14、Strategy模式
  • 簡介
    策略模式:對於某個需求時我們可能會使用一些算法或邏輯來解決,但因爲不同原用需求中算法需要被整體改變或替換時,一般的編程方式可能很難實現,但如果將每個固定的邏輯封裝成策略,將它與整體系統分離,在執行需要時傳入對應的策略,此時邏輯就好像一個固定的模塊功能,即可實現無限次數的替換;

  • 功能角色
    在這裏插入圖片描述

  • 使用實例

  1. 創建策略接口,內部聲明要執行的策略方法
interface Strategy {
    fun actionStrategy(number: Int): Int // 聲明的策略方法
}
  1. 分別實現不同的策略邏輯
class NumberStrategy : Strategy { // 第一種策略
    override fun actionStrategy(number: Int): Int {
        Log.e(" NumberStrategy = ", number.toString())
        return number
    }
}

class DoubleStrategy : Strategy { // 第二種策略
    override fun actionStrategy(number: Int): Int {
        return number * number
    }
}
  1. 使用策略模式
 val player = Palyer(NumberStrategy())
 player.play(5) 
 val playerD = Palyer(DoubleStrategy())
 playerD.play(5)
  • 意義:將程序中的部分邏輯單獨封裝成模塊,然後在使用時直接進行裝配或替換,從而達到不同的實現效果
  • 使用場景:需要根據不同條件整體替換算法和邏輯時,使用策略模式處理對應的邏輯模塊;
15、Template模式
  • 簡介
    模版模式:即所有的實現按照統一的方式進行,這裏統一的方式指固定的執行邏輯,模版模式首先在父類中藉助抽象方法實現邏輯的調用順序,然後在不同的子類中處理具體的抽象方法即可;

  • 功能角色
    在這裏插入圖片描述

  • 使用實例:以公司職員一天的工作爲例

  1. 聲明抽象方法和執行邏輯
abstract class AbstractTemplate {
    protected abstract fun weakUp() //起牀之後的事情
    protected abstract fun enteringTheOffice() //進入辦公室
    protected abstract fun openCompute() //打開電腦
    protected abstract fun doJob() //工作
    protected abstract fun goHome()  //回家

    fun actionForWorker() { // 方法中確定了執行的邏輯
        weakUp()
        enteringTheOffice()
        openCompute()
        doJob()
        goHome()
    }
}
  1. 針對不同的需求目標,實現抽象方法
class DesignWorker(val name: String) : AbstractTemplate() { // 設計師工作
    override fun weakUp() {
        Log.e("$name ------","8:00 起牀了!")
        Log.e("$name ------","8:10 刷牙洗臉!")
        Log.e("$name ------","8:20 健身!")
        Log.e("$name ------","8:40 吃早飯!")
        Log.e("$name ------","9:00 開車出發!")
    }
    override fun enteringTheOffice() {
        Log.e("$name ------","9:30 進入公司!")
        Log.e("$name ------","9:35 泡了杯咖啡!")
    }
    override fun openCompute() {
        Log.e("$name ------","9:40 看了場設計秀!")
    }
    override fun doJob() {
        Log.e("$name ------","10:30 開始設計!")
    }
    override fun goHome() {
        Log.e("$name ------","7:00 開車回家!")
    }
}

class ITWorker(val name: String) : AbstractTemplate() { // 程序員工作
    override fun weakUp() {
        Log.e("$name ------","8:30 起牀了!")
        Log.e("$name ------","9:00 刷牙洗臉,出發上班了!")
    }
    override fun enteringTheOffice() {
        Log.e("$name ------","9:30 進入公司!")
        Log.e("$name ------","9:35 接了杯白開水!")
    }
    override fun openCompute() {
        Log.e("$name ------","9:40 打開它的Mac Pro !")
    }
    override fun doJob() {
        Log.e("$name ------","9:40 開始一天的寫代碼 !")
    }
    override fun goHome() {
        Log.e("$name ------","7:00 開始下班坐地鐵回家 !")
    }
}
  1. 使用模版模式
        val itWorker = ITWorker("程序猿")
        val design = DesignWorker("設計師")
        itWorker.actionForWorker()
        design.actionForWorker()
  • 設計模式意義
    模版模式的優點在於只需在父類中編寫算法和處理流程,子類中無需編寫只需關注自身細節的處理,分離了整體邏輯和實現,提高代碼的可讀性也極大簡化了程序的修改和維護;

  • 使用場景:像例子一樣按照整體邏輯但針對不同的目標處理時,即可採用模版模式定義實現模版,然後各自實現細節

16、Visitor模式
  • 簡介
    訪問模式:由前面知道組合模式將具有一致性的數據遞歸保存在一起,但對於這些數據的訪問還是依賴於每個數據或容器,而訪問者模式在此基礎上將對數據的訪問和數據的結構分離出來,使訪問的邏輯或算法更容易修改;
  • 功能角色
    在這裏插入圖片描述
  • 使用實例
  1. 創建Visiter接口,針對不同的數據類型聲明和實現相應的方法
interface Visitor {
    fun visitor(file: FileElement)
    fun visitor(directory: DirectoryElement)
}

class ConcreteVisitor : Visitor {
    override fun visitor(file: FileElement) {
        Log.e("File=======", file.name)
    }
    override fun visitor(directory: DirectoryElement) {
        Log.e("Directory=======", directory.name)
        val iterator = directory.getInterator() // 對於文件夾循環遍歷內部文件
        while (iterator.hasNext()) {
            val file = iterator.next()
            file.accept(this)
        }
    }
}
  1. 創建數據結構,在數據結構中聲明訪問器
abstract class Element {
    var parent: Element? = null
    var name: String? = null
    abstract fun accept(visitor: Visitor)
}
  1. 實現具體的數據結構
class FileElement(val nameFile: String, val sizeFile: Int = 0, var parentFile: Element? = null) : Element() { //File
    override fun accept(visitor: Visitor) {
        visitor.visitor(this)
    }
    init {
        name = nameFile
        parent = parentFile
    }
}

class DirectoryElement(val nameFile: String, val parentFile: Element? = null) : Element() { //Directory
    override fun accept(visitor: Visitor) {
        visitor.visitor(this)
    }
    val arrayList = ArrayList<Element>()
    init {
        name = nameFile
        parent = parentFile
    }
    fun add(entry: Element) {
        entry.parent = this
        arrayList.add(entry)
    }
    fun getInterator() = arrayList.iterator()
}
  • 意義
    訪問模式實現了數據結構和數據訪問的分離,對於數據結構穩定但訪問算法經常改變的場景,此時使用訪問模式分離可變的處理邏輯,更好的體現了開閉原則;

  • 使用場景:數據結構穩定,但對數據的訪問和處理邏輯經常改變的場景

17、Observer模式
  • 簡介
    觀察者模式也是開發中經常接觸和使用的設計模式之一,在開源框架和Android系統組件中都有使用,典型代表RxJava、LiveData等;它的邏輯也很簡單主要將事件產生對象作爲被觀察者,將處理事件的邏輯作爲觀察者,二者採用註冊或訂閱的方式關聯,當事件發送時調用觀察者的方法處理事件;

  • 功能角色
    在這裏插入圖片描述

  • 使用實例:RxJava(Android框架源碼分析——RxJava源碼分析

  • 意義
    將整個邏輯的處理分爲觀察者和被觀察者兩部分,被觀察者只需發送事件無需知道誰觀察則自己,而觀察者只需處理事件也無需知道事件的來源,此時被觀察者和觀察者可以任意組合使用;

  • 使用場景:需要都某個過程實現訂閱,當有新的消息通知自動處理時使用觀察者;

18、Command模式
  • 簡介
    命令模式:將任務的執行向命令傳遞一樣發送,由指揮官發送命令,經過系列的傳達後到達目標士兵,士兵接收命令後執行,從這裏看出指揮官不一定知道他的命令最終經過多少傳遞和由誰執行,它只需要知道自己要下達那條命令,而士兵可能也不知道他的任務最初是誰的想法和命令,所以在程序中以此方式分離的發送命令和執行命令的角色;

  • 功能角色
    在這裏插入圖片描述

  • 使用實例

  1. 創建命令接口,聲明抽象方法
interface Command {
    fun action()
}
  1. 創建具體的命令方法和命令接收者,並在ActionCommand中傳入命令執行者Receiver
class Receiver {
    fun doAction(){......}
}

class ActionCommand(private val receiver: Receiver) : Command {
    override fun action() {
        receiver.doAction()
    }
}
  1. 創建命令Invoke類
class Invoke(private val command: Command) {
    fun doInvoke() {
        command.action()
    }
}
  1. 使用命令模式
        val receiver = Receiver()
        val command = ActionCommand(receiver)
        val invoke = Invoke(command)
        invoke.doInvoke()
  • 意義
    將命令發送者和執行者分離實現代碼解耦,在使用時可以爲每條命令選擇相應的執行者和發送者;

  • 使用場景:對於請求和執行相分離的場景

19、Memento模式
  • 簡介
    備忘錄模式:主要是通過增加類在關鍵時刻保存當前的狀態,防止程序在執行中需要恢復的需求,
  • 功能角色
    在這裏插入圖片描述
  • 使用實例
  1. 創建User的備忘錄Memento
class Memento(val money : Int) // 內部保存錢數
class User {
    private var money = 0
    private var memento: Memento = Memento(money)
    fun getMemory() {
        money = Random().nextInt()
    }
    fun createMementoInfo() { // 保存
        memento = Memento(money)
    }
    fun restoreMementoInfo() { // 恢復
        money = memento.money
    }
}
  • 意義:保存對象操作前的狀態實現撤銷操作的目的
20、Chain of Responsibility模式
  • 簡介
    責任鏈模式:將多個對象組成一個職責鏈,然後按照它們在職責鏈上的順序逐個找出負責處理的類,在使用時只需要將職責鏈按照一定的順序和條件組成後,後續的操作完全靠每個對象自己執行;
  • 功能角色
    在這裏插入圖片描述
  • 使用實例
  1. 創建基礎責任類和方法,在內部定義職責和執行邏輯
abstract class Support {
    private var next: Support? = null
    fun setNextSupport(support: Support): Support? { //設置下一個職責對象
        next = support
        return next
    }

    abstract fun action(number: Int) // 執行具體的操作
    abstract fun canResolve(number: Int): Boolean // 判斷是否符合執行條件
    fun doAction(number: Int) {
        if (canResolve(number)) {
            action(number)
        } else if (next == null) {
            doFailed()
        } else {
            next?.doAction(number)
        }
    }
    fun doFailed() { // 當沒有方法處理時
        System.out.println("No Support")
    }
}
  1. 實現不同的職責類,並設置每個職責類的條件
class SupportFirst : Support() {
    override fun action(number: Int) {
      System.out.println("SupportFirst")
    }
    override fun canResolve(number: Int): Boolean {
       return number < 10
    }
}

class SupportSecond(val numberMax : Int) : Support() {
    override fun action(number: Int) {
      System.out.println("SupportFirst")
    }
    override fun canResolve(number: Int): Boolean {
       return number in 10..numberMax
    }
}
  1. 使用設計模式
        val supportFirst = SupportFirst() // 創建職責對象
        val supportSecond = SupportSecond(20)
        val supportThird = SupportSecond(30)
        supportFirst.setNextSupport(supportSecond).setNextSupport(supportThird) //設置職責鏈
        supportFirst.action(25) // 調用方法
  • 意義:通過使用多個職責對象形成責任鏈的形式,弱化了發送請求對象和處理請求對之間的關係,對於每個職責類來看,只需要關注自身的職責即處理條件和處理方式,而整個職責鏈的設置和調用順序也可以隨時修改和增減;

  • 使用場景:當需要針對多種條件分別處理邏輯時使用職責模式;

21、State模式
  • 簡介
    狀態模式很好理解,對象的本身可能會有多種狀態存在,處在不同的狀態時對象的屬性和操作都有所不同,狀態模式就是指針對當前狀態下的所有操作進行編程,然後在使用時根據具體情況切換狀態即可

  • 功能角色
    在這裏插入圖片描述

  • 使用實例:以開關等爲例

  1. 定義狀態接口
interface State {
    fun openLight(light: Light) // 開燈
    fun closeLight(light: Light) // 關燈
}
  1. 分別實現不同狀態下的操作
class StateClose : State { // 關燈狀態
    companion object {
        val close = StateClose()
    }
    override fun openLight(light: Light) {
        light.isLight = true
        System.out.println("打開燈了")
        light.steState(StateOpen.open)
    }

    override fun closeLight(light: Light) {
        System.out.println("燈還沒開")
    }
}

class StateOpen : State { // 開燈狀態
    companion object {
        val open = StateOpen()
    }
    override fun openLight(light: Light) {
        System.out.println("燈已經開了")
    }
    override fun closeLight(light: Light) {
        light.isLight = false
        System.out.println("燈關閉了")
        light.steState(StateClose.close)
    }
}
  1. 創建燈類,並提高狀態保存
class Light {
    var isLight = false
    private var state: State = StateClose.close

    fun steState(state: State) {
        this.state = state
    }
    fun open() {
        state.openLight(this)
    }
    fun close() {
        state.closeLight(this)
    }
}
  1. 使用
        val light = Light()
        light.open()
        light.open()
        light.close()
        light.close()
  • 意義:使用狀態模式,將所有的操作以不同的狀態呈現,在每次操作完成後修改當前狀態,避免了每個操作中都需要大量的條件判斷,提高程序的可讀性和擴展性

  • 使用場景:針對不同狀態有不同操作的場景

22、Mediator模式
  • 簡介
    中介者模式:當兩個類產生相互依賴時程序的耦合度就會增加,相應的程序的擴展性就會減少,而中介者模式採用中間類的方式避免了類之間的直接依賴,當程序修改時只需要處理好和中間類之間的關係即可,提高了程序的可維護性;
  • 功能角色
    在這裏插入圖片描述
  • 使用實例
  1. 創建方法接口和實現類
class First : Action {
    override fun action() {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}

class Second : Action {
    override fun action() {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}
  1. 創建中介者類,內部持有上面的兩個類對象
class Meidator {
    val first = First()
    val second = Second()
    fun doAction() { 
        first.action()
        second.action()
    }
}
  1. 使用
        val meidator = Meidator()
        meidator.doAction()
  • 意義:將類之間的依賴關係解耦,提高程序的可維護性

  • 使用場景:當現有的工具或代碼不能單獨實現邏輯時,此時使用中介者模式組合使用代碼,在中介者中實現代碼調用;

23、Interpreter模式
  • 簡介
    按照一定的語法定義一個解釋器,在使用時通過多個解釋器的持有執行整體的業務邏輯;
  • 功能角色
    在這裏插入圖片描述
  • 使用實例:Okhttp
  • 意義 : 將程序中對某個細節的處理單獨抽取出來作爲解釋器,有利於快速修改個更換解釋模塊功能,在使用時通過相互持有的方式將多個解釋器組合在一起按照順序執行;
  • 使用場景:在程序中需要多個環節處理時,可以將每個部分封裝成Interpreter,然後組合使用

到此Java中的23種設計模式就介紹完畢了,對設計模式的學習相信很多人的感觸都是一致的,學習的時候發現設計模式很奇妙,也確實很有設計感,但真正使用時還是不太容易正確的選擇和使用,這也是對設計模式的理解不夠深入吧,希望通過此23
中設計模式的學習能更好的掌握並使用設計模式;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章