1. 程式人生 > >聊聊那些專為演算法設計的模式——訪問模式

聊聊那些專為演算法設計的模式——訪問模式

AI越來越火熱,人工智慧已然成風!而人工智慧最重要是各種演算法,因此機器學習越來越受到追捧,演算法越來越被重視。

作為一個演算法的研究者,寫出一手高階演算法當然是令人興奮的一件事!但你是否有時會有這種感覺: 1. 寫的演算法很難通用於所有的資料型別!每來一個新型別的資料,又得改一下演算法,或新加一個方法來支援這種型別。 2. 有時候多個演算法需要靈活組合,甚至每個演算法的順序不一樣都會產生不一樣的效果;每一種組合都要為其構建一個新演算法,即累又麻煩。 3. 演算法越來越多,自建的演算法庫也越來越龐大而難於管理;

這個時候,讓你的演算法具有更好通用性、拓展性就顯得極為重要!因此,你必須要掌握幾個重要的設計模式來優化你的程式碼,解決這些問題。今天就來聊聊那些專為演算法設計的模式:策略模式、模板方法模式、訪問模式。

訪問模式

封裝一些作用於某種資料結構中各元素的操作,它可以在不改變資料結構的前提下定義作用於這些元素的新的操作。

訪問模式的核心思想在於:可以在不改變資料結構的前提下定義作用於這些元素的新操作。將資料結構和具體演算法進行解耦,而且能更方便地拓展新的操作。

訪問模式的程式碼框架

from abc import ABCMeta, abstractmethod
# 引入ABCMeta和abstractmethod來定義抽象類和抽象方法

class DataNode(metaclass=ABCMeta):
    "資料結構類"

    def accept(self, visitor)
:
"接受訪問者的訪問" visitor.visit(self) class Visitor(metaclass=ABCMeta): "訪問者" @abstractmethod def visit(self, data): "對資料物件的訪問操作" pass class ObjectStructure: "資料結構的管理類,也是資料物件的一個容器,可遍歷容器內的所有元素" def __init__(self): self.__datas = [] def add
(self, dataElement):
self.__datas.append(dataElement) def action(self, visitor): "進行資料訪問的操作" for data in self.__datas: visitor.visit(data)

這裡Visitor的訪問方法只有一個visit(),是因為Python不支援方法的過載。在一些強型別的語言(如Java、C++)中,應該有多個方法,針對每一個DataNode子類定義一個過載方法。

訪問模式的類圖結構:

Alt text DataNode是資料結點,可接受(accept)訪問者的訪問,DataNodeA和DataNodeB是它的具體實現類。Visitor是訪問者類,可訪問(visit)具體的物件。ObjectStructure是資料結構的管理類,也是資料物件的一個容器,可遍歷容器內的所有元素。

應用案例

在寵物界中,貓和狗歷來就是一對歡喜冤家!假設寵物店中有N只貓和M只狗。我們要進行下面這3個操作: 1. 在這些寵物中雌貓、雄貓、雌狗、雄狗的數量分別是多少。 2. 貓的平均體重和狗的平均體重分別是多少。

這個時候,如果要在貓和狗的物件上新增這些操作,將會增加非常多的方法而汙染原有的物件;而且這些操作的拓展性也將非常差。這時訪問模式是解決這個問題的最好方法,我們一起看一下具體的實現如下:

原始碼示例:

class Animal(DataNode):
    """動物類"""

    def __init__(self, isMale, weight):
        self.__isMale = isMale
        self.__weight = weight

    def isMale(self):
        return self.__isMale

    def getWeight(self):
        return self.__weight


class Cat(Animal):
    """貓"""

    def speak(self):
        print("miao~")


class Dog(Animal):
    """狗"""

    def speak(self):
        print("wang~")


class GenderCounter(Visitor):
    """性別統計"""

    def __init__(self):
        self.__maleCat = 0
        self.__femaleCat = 0
        self.__maleDog = 0
        self.__femalDog = 0

    def visit(self, data):
        if isinstance(data, Cat):
            if data.isMale():
                self.__maleCat += 1
            else:
                self.__femaleCat += 1
        elif isinstance(data, Dog):
            if data.isMale():
                self.__maleDog += 1
            else:
                self.__femalDog += 1
        else:
            print("Not support this type")

    def getInfo(self):
        print(str(self.__maleCat) + "只雄貓," + str(self.__femaleCat) + "只雌貓,"
              + str(self.__maleDog) + "只雄狗," + str(self.__femalDog) + "只雌狗。")

class WeightCounter(Visitor):
    """體重的統計"""

    def __init__(self):
        self.__catNum = 0
        self.__catWeight = 0
        self.__dogNum = 0
        self.__dogWeight  = 0

    def visit(self, data):
        if isinstance(data, Cat):
            self.__catNum +=1
            self.__catWeight += data.getWeight()
        elif isinstance(data, Dog):
            self.__dogNum += 1
            self.__dogWeight += data.getWeight()
        else:
            print("Not support this type")

    def getInfo(self):
        print("貓的平均體重是:%0.2fkg, 狗的平均體重是:%0.2fkg" %
              ((self.__catWeight / self.__catNum),(self.__dogWeight / self.__dogNum)))

測試程式碼:

def testAnimal():
    animals = ObjectStructure()
    animals.add(Cat(True, 5.1))
    animals.add(Cat(False, 4.3))
    animals.add(Dog(True, 8))
    animals.add(Dog(False, 21))
    animals.add(Dog(False, 25))

    genderCounter = GenderCounter()
    animals.action(genderCounter)
    genderCounter.getInfo()
    print()
    weightCounter = WeightCounter()
    animals.action(weightCounter)
    weightCounter.getInfo()
    print()

輸出結果:

1只雄貓,1只雌貓,1只雄狗,2只雌狗。

貓的平均體重是:4.70kg, 狗的平均體重是:18.00kg

訪問模式將“資料”和“操作演算法”分離,降低了耦合度。將相關元素物件的訪問行為集中到一個訪問者物件中,而不是分散在一個個的元素類中,類的職責更加清晰;操作演算法更易拓展。訪問模式特別適合應用於以下場景:物件結構中包含的物件型別比較少,而且這些類需要比較固定,很少改變,但經常需要在此物件結構上定義新的操作。

Get到這些技能點了嗎?

目錄

引導篇

基礎篇

進階篇

經驗篇