欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python中的SOLID原則實(shí)例詳解

 更新時(shí)間:2023年02月15日 09:09:58   作者:海擁?  
SOLID原則是由Robert C. Martin提出的以首字母縮寫命名的編碼準(zhǔn)則,它代表了五種不同的編碼習(xí)慣,下面這篇文章主要給大家介紹了關(guān)于Python中SOLID原則的相關(guān)資料,需要的朋友可以參考下

前言

SOLID 是一組面向?qū)ο蟮脑O(shè)計(jì)原則,旨在使代碼更易于維護(hù)和靈活。它們是由 Robert “Uncle Bob” Martin 于 2000 年在他的論文 設(shè)計(jì)原則和設(shè)計(jì)模式中創(chuàng)造的。SOLID 原則適用于任何面向?qū)ο蟮恼Z(yǔ)言,但在本文中我將重點(diǎn)關(guān)注它們?cè)?Python 應(yīng)用程序中的含義。

我最初以 PHP 為基礎(chǔ)撰寫有關(guān) SOLID 原則的文章,但由于此處的課程可以輕松應(yīng)用于任何面向?qū)ο蟮恼Z(yǔ)言,我認(rèn)為我會(huì)考慮使用 Python 重新編寫它。如果您只熟悉 PHP 或 Python,那么這將是學(xué)習(xí)另一面的一個(gè)很好的學(xué)習(xí)資源。

在這里我們還應(yīng)該注意,Python 并沒(méi)有真正的接口系統(tǒng),所以我使用元類來(lái)創(chuàng)建所需的情況。有關(guān)元類的更多說(shuō)明,請(qǐng)參閱Python 中面向?qū)ο缶幊倘腴T文章的基礎(chǔ)知識(shí)中的接口部分。

SOLID 是一個(gè)首字母縮寫詞,代表以下內(nèi)容:

  • 單一職責(zé)原則
  • 開放/封閉原則
  • Liskov替代原則
  • 接口隔離原則
  • 依賴倒置原則

我們將依次解析它們。

單一職責(zé)原則

這表明一個(gè)類應(yīng)該有單一的責(zé)任,但更重要的是,一個(gè)類應(yīng)該只有一個(gè)改變的理由。

以名為Page的(簡(jiǎn)單)類為例。

import json

class Page():
    def __init__(self, title):
        self._title = title

    def get_title(self):
        return self._title

    def set_title(self, title):
        self._title = title

    def get_page(self):
        return [self._title]

    def format_json(self):
        return json.dumps(self.get_page())

此類知道 title 屬性并允許通過(guò) get() 方法檢索此 title 屬性。我們還可以使用此類中名為 format_json() 的方法將頁(yè)面作為 JSON 字符串返回。這似乎是個(gè)好主意,因?yàn)轭愗?fù)責(zé)自己的格式。

但是,如果我們想要更改 JSON 字符串的輸出,或者向類中添加另一種類型的輸出,會(huì)發(fā)生什么情況呢?我們需要更改類以添加另一個(gè)方法或更改現(xiàn)有方法以適應(yīng)。這對(duì)于像這樣簡(jiǎn)單的類來(lái)說(shuō)很好,但如果它包含更多屬性,那么更改格式將更加復(fù)雜。

一個(gè)更好的方法是修改Page類,這樣它只知道數(shù)據(jù)是句柄。然后我們創(chuàng)建一個(gè)名為JsonPageFormatter的輔助類,用于將Page對(duì)象格式化為 JSON。

import json

class Page():
    def __init__(self, title):
        self._title = title

    def get_title(self):
        return self._title

    def set_title(self, title):
        self._title = title

    def get_page(self):
        return [self._title]

class JsonPageFormatter():
    def format_json(page: Page):
        return json.dumps(page.get_page())

這樣做意味著如果我們想創(chuàng)建一個(gè) XML 格式,我們只需添加一個(gè)名為XmlPageFormatter的類并編寫一些簡(jiǎn)單的代碼來(lái)輸出 XML。我們現(xiàn)在只有一個(gè)理由來(lái)更改Page類。

開閉原則

在開閉原則中,類應(yīng)該 對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。本質(zhì)上意味著類應(yīng)該被擴(kuò)展以改變功能,而不是被改變成其他東西。

以下面兩個(gè)類為例。

class Rectangle():
    def __init__(self, width, height):
        self._width = width
        self._height = height

    def get_width(self):
        return self._width

    def set_width(self, width):
        self._width = width

    def get_height(self):
        return self._height

    def set_height(self, height):
        self._height = height

class Board():
    @property
    def rectangles(self):
        return self._rectangles

    @rectangles.setter
    def rectangles(self, value):
        self._rectangles = value  

    def calculateArea(self):
        area = 0
        for item in self.rectangles:
            area += item.get_height() * item.get_width()
        return area

我們有一個(gè)包含矩形數(shù)據(jù)的Rectangle類,以及一個(gè)用作Rectangle對(duì)象集合的Board類。使用此設(shè)置,我們可以通過(guò)循環(huán)遍歷rectangles集合屬性中的項(xiàng)目并計(jì)算它們的面積來(lái)輕松找出板的面積。

此設(shè)置的問(wèn)題在于我們受到可以傳遞給Board類的對(duì)象類型的限制。例如,如果我們想將一個(gè)Circle對(duì)象傳遞給Board類,我們需要編寫條件語(yǔ)句和代碼來(lái)檢測(cè)和計(jì)算Board的面積。

解決這個(gè)問(wèn)題的正確方法是將面積計(jì)算代碼移到形狀類中,并讓所有形狀類都擴(kuò)展一個(gè)Shape接口。我們現(xiàn)在可以創(chuàng)建一個(gè)Rectangle和Circle形狀類,它們將在被要求時(shí)計(jì)算它們的面積。

import math

class ShapeMeta(type):
    def __instancecheck__(self, instance):
        return self.__subclasscheck__(type(instance))

    def __subclasscheck__(self, subclass):
        return (hasattr(subclass, 'area') and callable(subclass.area))

class ShapeInterface(metaclass=ShapeMeta):
    pass

class Rectangle(ShapeInterface):
    def __init__(self, width, height):
        self._width = width
        self._height = height

    def get_width(self):
        return self._width

    def set_width(self, width):
        self._width = width

    def get_height(self):
        return self._height

    def set_height(self, height):
        self._height = height

    def area(self):
        return self.get_width() * self.get_height()

class Circle(ShapeInterface):
    def __init__(self, radius):
        self._radius = radius

    def get_radius(self):
        return self._radius

    def set_radius(self, radius):
        self._radius = radius

    def area(self):
        return self.get_radius() * self.get_radius() * math.pi

現(xiàn)在 可以重新設(shè)計(jì)Board類,使其不關(guān)心傳遞給它的形狀類型,只要它們實(shí)現(xiàn) area() 方法即可。

class Board():
    def __init__(self, shapes):
        self._shapes = shapes

    def calculateArea(self):
        area = 0
        for shape in self._shapes:
            area += shape.area()
        return area

我們現(xiàn)在已經(jīng)設(shè)置了這些對(duì)象,這意味著如果我們有不同類型的對(duì)象,我們不需要改變Board類。我們只是創(chuàng)建實(shí)現(xiàn)Shape的對(duì)象,并以與其他類相同的方式將其傳遞到集合中。

里氏替換原則

由 Barbara Liskov 在 1987 年創(chuàng)建,它指出對(duì)象應(yīng)該可以被它們的子類型替換而不改變程序的工作方式。換句話說(shuō),派生類必須可以替代它們的基類而不會(huì)導(dǎo)致錯(cuò)誤。

下面的代碼定義了一個(gè)Rectangle類,我們可以用它來(lái)創(chuàng)建和計(jì)算矩形的面積。

class Rectangle():
    def __init__(self, width, height):
        self._width = width
        self._height = height

    def get_width(self):
        return self._width

    def set_width(self, width):
        self._width = width

    def get_height(self):
        return self._height

    def set_height(self, height):
        self._height = height

    def area(self):
        return self.get_width() * self.get_height()

使用它,我們可以將其擴(kuò)展為Square類。因?yàn)檎叫闻c矩形略有不同,我們需要重寫一些代碼以允許正方形正確存在。

class Square(Rectangle):
    def __init__(self, width):
        self._width = width
        self._height = width

    def get_width(self):
        return self._width

    def set_width(self, width):
        self._width = width
        self._height = width

    def get_height(self):
        return self._height

    def set_height(self, height):
        self._height = height
        self._width = height

這看起來(lái)不錯(cuò),但最終正方形不是矩形,因此我們添加了代碼來(lái)強(qiáng)制這種情況起作用。

我讀過(guò)的一個(gè)很好的類比是考慮類代表的鴨子和橡皮鴨。盡管可以將 Duck 類擴(kuò)展為 Rubber Duck 類,但我們需要重寫許多 Duck 功能以適應(yīng) Rubber Duck。例如,鴨子嘎嘎叫,但橡皮鴨不叫(好吧,也許它會(huì)吱吱叫),鴨子是活的,但橡皮鴨不是。

覆蓋類中的大量代碼以適應(yīng)特定情況可能會(huì)導(dǎo)致維護(hù)問(wèn)題。您為覆蓋特定條件而添加的代碼越多,您的代碼就會(huì)變得越脆弱。

矩形與正方形情況的一種解決方案是創(chuàng)建一個(gè)名為Quadrilateral的接口,并在單獨(dú)的Rectangle和Square 類中實(shí)現(xiàn)它。在這種情況下,我們?cè)试S類負(fù)責(zé)它們自己的數(shù)據(jù),但強(qiáng)制要求某些方法足跡可用。

class QuadrilateralMeta(type):
    def __instancecheck__(self, instance):
        return self.__subclasscheck__(type(instance))

    def __subclasscheck__(self, subclass):
        return (hasattr(subclass, 'area') and callable(subclass.area)) \
          and (hasattr(subclass, 'get_height') and callable(subclass.get_height)) \
          and (hasattr(subclass, 'get_width') and callable(subclass.get_width)) \

class QuadrilateralInterface(metaclass=QuadrilateralMeta):
    pass

class Rectangle(QuadrilateralInterface):
    pass

class Square(QuadrilateralInterface):
    pass

這里的底線是,如果你發(fā)現(xiàn)你覆蓋了很多代碼,那么你的架構(gòu)可能是錯(cuò)誤的,你應(yīng)該考慮 Liskov 替換原則。

接口隔離原則

這表明許多特定于客戶端的接口優(yōu)于一個(gè)通用接口。換句話說(shuō),不應(yīng)強(qiáng)制類實(shí)現(xiàn)它們不使用的接口。

讓我們以Worker接口為例。這定義了幾種不同的方法,可以應(yīng)用于典型開發(fā)機(jī)構(gòu)的工作人員。

class WorkerMeta(type):
    def __instancecheck__(self, instance):
        return self.__subclasscheck__(type(instance))

    def __subclasscheck__(self, subclass):
        return (hasattr(subclass, 'take_break') and callable(subclass.take_break)) \
          and (hasattr(subclass, 'write_code') and callable(subclass.write_code)) \
          and (hasattr(subclass, 'call_client') and callable(subclass.call_client)) \
          and (hasattr(subclass, 'get_paid') and callable(subclass.get_paid))

class WorkerInterface(metaclass=WorkerMeta):
    pass

問(wèn)題是因?yàn)檫@個(gè)接口太通用了,我們不得不在實(shí)現(xiàn)這個(gè)接口的類中創(chuàng)建方法來(lái)適應(yīng)這個(gè)接口。

例如,如果我們創(chuàng)建一個(gè)Manager類,那么我們將被迫實(shí)現(xiàn)一個(gè) write_code() 方法,因?yàn)檫@是接口所需要的。因?yàn)榻?jīng)理通常不編寫代碼,所以我們實(shí)際上無(wú)法在此方法中執(zhí)行任何操作,因此我們只返回 false。

class Manager(WorkerInterface):
    def write_code(self):
        pass

此外,如果我們有一個(gè)實(shí)現(xiàn)Worker的Developer類,那么我們將被迫實(shí)現(xiàn)一個(gè) call_client() 方法,因?yàn)檫@是接口所需要的。

class Developer(WorkerInterface):
    def call_client(self):
        pass

擁有一個(gè)臃腫的接口意味著必須實(shí)現(xiàn)什么都不做的方法。

正確的解決方案是將我們的界面拆分成單獨(dú)的部分,每個(gè)部分處理特定的功能。在這里,我們從我們的通用Worker接口中分離出Coder和ClientFacer接口。

class WorkerMeta(type):
    def __instancecheck__(self, instance):
        return self.__subclasscheck__(type(instance))

    def __subclasscheck__(self, subclass):
        return (hasattr(subclass, 'take_break') and callable(subclass.take_break)) \
          and (hasattr(subclass, 'get_paid') and callable(subclass.get_paid))

class WorkerInterface(metaclass=WorkerMeta):
    pass


class ClientFacerMeta(type):
    def __instancecheck__(self, instance):
        return self.__subclasscheck__(type(instance))

    def __subclasscheck__(self, subclass):
        return (hasattr(subclass, 'call_client') and callable(subclass.call_client))

class ClientFacerInterface(metaclass=ClientFacerMeta):
    pass


class CoderMeta(type):
    def __instancecheck__(self, instance):
        return self.__subclasscheck__(type(instance))

    def __subclasscheck__(self, subclass):
        return (hasattr(subclass, 'write_code') and callable(subclass.write_code))

class CoderInterface(metaclass=CoderMeta):
    pass

有了這個(gè),我們就可以實(shí)現(xiàn)我們的子類,而不必編寫我們不需要的代碼。所以我們的Developer和Manager類看起來(lái)像這樣。

class Manager(WorkerInterface, ClientFacerInterface):
    pass

class Developer(WorkerInterface, CoderInterface):
    pass

擁有許多特定接口意味著我們不必編寫代碼來(lái)支持接口。

依賴倒置原則

也許是最簡(jiǎn)單的原則,它指出類應(yīng)該依賴于抽象,而不是具體化。本質(zhì)上,不依賴于具體類,依賴于接口。

以使用MySqlConnection類從數(shù)據(jù)庫(kù)加載頁(yè)面的PageLoader類為例,我們可以創(chuàng)建這些類,以便將連接類傳遞給PageLoader類的構(gòu)造函數(shù)。

class MySqlConnection():
    def connect(self):
        pass

class PageLoader():
    def __init__(self, mysql_connection: MySqlConnection):
        self._mysql_connection = mysql_connection

這種結(jié)構(gòu)意味著我們基本上只能在數(shù)據(jù)庫(kù)層使用 MySQL。如果我們想將其換成不同的數(shù)據(jù)庫(kù)適配器會(huì)怎樣?我們可以擴(kuò)展MySqlConnection類以創(chuàng)建到 Memcache 或其他東西的連接,但這會(huì)違反 Liskov 替換原則??赡軙?huì)使用備用數(shù)據(jù)庫(kù)管理器來(lái)加載頁(yè)面,因此我們需要找到一種方法來(lái)執(zhí)行此操作。

這里的解決方案是創(chuàng)建一個(gè)名為DbConnectionInterface的接口,然后在MySqlConnection類中實(shí)現(xiàn)這個(gè)接口。然后,我們不再依賴傳遞給PageLoader類的MySqlConnection對(duì)象,而是依賴任何實(shí)現(xiàn)DbConnectionInterface接口的類。

class DbConnectionMeta(type):
    def __instancecheck__(self, instance):
        return self.__subclasscheck__(type(instance))

    def __subclasscheck__(self, subclass):
        return (hasattr(subclass, 'connect') and callable(subclass.connect))

class DbConnectionInterface(metaclass=DbConnectionMeta):
    pass


class MySqlConnection(DbConnectionInterface):
    def connect(self):
        pass

class PageLoader():
    def __init__(self, db_connection: DbConnectionInterface):
        self._db_connection = db_connection

有了這個(gè),我們現(xiàn)在可以創(chuàng)建一個(gè)MemcacheConnection類,只要它實(shí)現(xiàn)了DbConnectionInterface,我們就可以在PageLoader類中使用它來(lái)加載頁(yè)面。

這種方法還迫使我們以這樣一種方式編寫代碼,以防止不關(guān)心它的類中的特定實(shí)現(xiàn)細(xì)節(jié)。因?yàn)槲覀円呀?jīng)將MySqlConnection類傳遞給了PageLoader類,所以我們不應(yīng)該在PageLoader類 中編寫 SQL 查詢。這意味著當(dāng)我們傳入MemcacheConnection對(duì)象時(shí),它的行為方式與任何其他類型的連接類相同。

當(dāng)考慮接口而不是類時(shí),它迫使我們將特定域代碼移出我們的PageLoader類并移入MySqlConnection類。

如何發(fā)現(xiàn)它?

一個(gè)更大的問(wèn)題可能是,如果您需要將 SOLID 原則應(yīng)用于您的代碼,或者您正在編寫的代碼不是 SOLID,您如何才能發(fā)現(xiàn)。

了解這些原則只是成功的一半,您還需要知道什么時(shí)候應(yīng)該退后一步并考慮應(yīng)用 SOLID 原則。我想出了一個(gè)快速列表,列出了您需要關(guān)注的“告訴”,表明您的代碼可能需要重新編寫。

  • 您正在編寫大量“if”語(yǔ)句來(lái)處理目標(biāo)代碼中的不同情況。
  • 你寫了很多代碼,實(shí)際上并沒(méi)有做任何事情只是為了滿足界面設(shè)計(jì)。
  • 你一直打開同一個(gè)類來(lái)更改代碼。
  • 您在與該類沒(méi)有任何關(guān)系的類中編寫代碼。例如,將 SQL 查詢放在數(shù)據(jù)庫(kù)連接類之外的類中。

結(jié)論

SOLID 不是一種完美的方法,它可能會(huì)導(dǎo)致包含許多移動(dòng)部件的復(fù)雜應(yīng)用程序,并且偶爾會(huì)導(dǎo)致編寫代碼以備不時(shí)之需。使用 SOLID 意味著編寫更多類并創(chuàng)建更多接口,但許多現(xiàn)代 IDE 將通過(guò)自動(dòng)代碼完成來(lái)解決該問(wèn)題。

也就是說(shuō),它確實(shí)會(huì)迫使您分離關(guān)注點(diǎn)、考慮繼承、防止重復(fù)代碼并謹(jǐn)慎編寫應(yīng)用程序。畢竟,考慮對(duì)象如何在應(yīng)用程序中組合在一起是面向?qū)ο蟠a的全部?jī)?nèi)容。

到此這篇關(guān)于Python中的SOLID原則詳解的文章就介紹到這了,更多相關(guān)Python SOLID原則內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 弄清Pytorch顯存的分配機(jī)制

    弄清Pytorch顯存的分配機(jī)制

    這篇文章主要介紹了Pytorch顯存的分配機(jī)制的相關(guān)資料,幫助大家更好的理解和使用Pytorch,感興趣的朋友可以了解下
    2020-12-12
  • Python3.5文件讀與寫操作經(jīng)典實(shí)例詳解

    Python3.5文件讀與寫操作經(jīng)典實(shí)例詳解

    這篇文章主要介紹了Python3.5文件讀與寫操作,結(jié)合實(shí)例形式詳細(xì)分析了Python針對(duì)文件的讀寫操作常用技巧與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-05-05
  • python刪除不需要的python文件方法

    python刪除不需要的python文件方法

    下面小編就為大家分享一篇python刪除不需要的python文件方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • Tensorflow訓(xùn)練模型越來(lái)越慢的2種解決方案

    Tensorflow訓(xùn)練模型越來(lái)越慢的2種解決方案

    今天小編就為大家分享一篇Tensorflow訓(xùn)練模型越來(lái)越慢的2種解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-02
  • python 實(shí)現(xiàn)存儲(chǔ)數(shù)據(jù)到txt和pdf文檔及亂碼問(wèn)題的解決

    python 實(shí)現(xiàn)存儲(chǔ)數(shù)據(jù)到txt和pdf文檔及亂碼問(wèn)題的解決

    這篇文章主要介紹了python 實(shí)現(xiàn)存儲(chǔ)數(shù)據(jù)到txt和pdf文檔及亂碼問(wèn)題的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-03-03
  • python3爬蟲怎樣構(gòu)建請(qǐng)求header

    python3爬蟲怎樣構(gòu)建請(qǐng)求header

    在本篇內(nèi)容里小編給大家分享了關(guān)于python3爬蟲怎樣構(gòu)建請(qǐng)求header的知識(shí)點(diǎn),需要的朋友們學(xué)習(xí)下。
    2018-12-12
  • 使用Py2Exe for Python3創(chuàng)建自己的exe程序示例

    使用Py2Exe for Python3創(chuàng)建自己的exe程序示例

    今天小編就為大家分享一篇使用Py2Exe for Python3創(chuàng)建自己的exe程序示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-10-10
  • Python判斷直線和矩形是否相交的方法

    Python判斷直線和矩形是否相交的方法

    這篇文章主要介紹了Python判斷直線和矩形是否相交的方法,涉及Python坐標(biāo)系下的直線與矩形相關(guān)運(yùn)算,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • 修改 CentOS 6.x 上默認(rèn)Python的方法

    修改 CentOS 6.x 上默認(rèn)Python的方法

    這篇文章主要介紹了修改 CentOS 6.x 上默認(rèn)Python的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Django中如何使用Channels功能

    Django中如何使用Channels功能

    這篇文章主要介紹了在Django中使用Channels功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08

最新評(píng)論