中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

如何寫一手漂亮的模型:面向?qū)ο缶幊痰脑O(shè)計(jì)原則綜述

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

面向?qū)ο蟮木幊淘趯?shí)現(xiàn)想法乃至系統(tǒng)的過程中都非常重要,我們不論是使用 TensorFlow 還是 PyTorch 來構(gòu)建模型都或多或少需要使用類和方法。而采用類的方法來構(gòu)建模型會(huì)令代碼非常具有可讀性和條理性,本文介紹了算法實(shí)現(xiàn)中使用類和方法來構(gòu)建模型所需要注意的設(shè)計(jì)原則,它們可以讓我們的機(jī)器學(xué)習(xí)代碼更加美麗迷人。

大多數(shù)現(xiàn)代編程語言都支持并且鼓勵(lì)面向?qū)ο缶幊蹋∣OP)。即使我們最近似乎看到了一些偏離,因?yàn)槿藗冮_始使用不太受 OOP 影響的編程語言(例如 Go, Rust, Elixir, Elm, Scala),但是大多數(shù)還是具有面向?qū)ο蟮膶傩。我們(cè)谶@里概括出的設(shè)計(jì)原則也適用于非 OOP 編程語言。

為了成功地寫出清晰的、高質(zhì)量的、可維護(hù)并且可擴(kuò)展的代碼,我們需要以 Python 為例了解在過去數(shù)十年里被證明是有效的設(shè)計(jì)原則。

對(duì)象類型

因?yàn)槲覀円獓@對(duì)象來建立代碼,所以區(qū)分它們的不同責(zé)任和變化是有用的。一般來說,面向?qū)ο蟮木幊逃腥N類型的對(duì)象。

1. 實(shí)體對(duì)象

這類對(duì)象通常對(duì)應(yīng)著問題空間中的一些現(xiàn)實(shí)實(shí)體。比如我們要建立一個(gè)角色扮演游戲(RPG),那么簡單的 Hero 類就是一個(gè)實(shí)體對(duì)象。

class Hero:
    def __init__(self, health, mana):
        self._health = health
        self._mana = mana

    def attack(self) -> int:
        """
        Returns the attack damage of the Hero
        """
        return 1

    def take_damage(self, damage: int):
        self._health -= damage

    def is_alive(self):
        return self._health > 0

這類對(duì)象通常包含關(guān)于它們自身的屬性(例如 health 或 mana),這些屬性根據(jù)具體的規(guī)則都是可修改的。

2. 控制對(duì)象(Control Object)

控制對(duì)象(有時(shí)候也稱作管理對(duì)象)主要負(fù)責(zé)與其它對(duì)象的協(xié)調(diào),這是一些管理并調(diào)用其它對(duì)象的對(duì)象。我們上面的 RPG 案例中有一個(gè)很棒的例子,F(xiàn)ight 類控制兩個(gè)英雄,并讓它們對(duì)戰(zhàn)。

class Fight:
    class FightOver(Exception):
        def __init__(self, winner, *args, **kwargs):
            self.winner = winner
            super(*args, **kwargs)

    def __init__(self, hero_a: Hero, hero_b: Hero):
        self._hero_a = hero_a
        self._hero_b = hero_b
        self.fight_ongoing = True
        self.winner = None

    def fight(self):
        while self.fight_ongoing:
            self._run_round()
        print(f'The fight has ended! Winner is #{self.winner}')

    def _run_round(self):
        try:
            self._run_attack(self._hero_a, self._hero_b)
            self._run_attack(self._hero_b, self._hero_a)
        except self.FightOver as e:
            self._finish_round(e.winner)

    def _run_attack(self, attacker: Hero, victim: Hero):
        damage = attacker.attack()
        victim.take_damage(damage)
        if not victim.is_alive():
            raise self.FightOver(winner=attacker)

    def _finish_round(self, winner: Hero):
        self.winner = winner
        self.fight_ongoing = False

在這種類中,為對(duì)戰(zhàn)封裝編程邏輯可以給我們提供多個(gè)好處:其中之一就是動(dòng)作的可擴(kuò)展性。我們可以很容易地將參與戰(zhàn)斗的英雄傳遞給非玩家角色(NPC),這樣它們就能利用相同的 API。我們還可以很容易地繼承這個(gè)類,并復(fù)寫一些功能來滿足新的需要。

3. 邊界對(duì)象(Boundary Object)

這些是處在系統(tǒng)邊緣的對(duì)象。任何一個(gè)從其它系統(tǒng)獲取輸入或者給其它系統(tǒng)產(chǎn)生輸出的對(duì)象都可以被歸類為邊界對(duì)象,無論那個(gè)系統(tǒng)是用戶,互聯(lián)網(wǎng)或者是數(shù)據(jù)庫。

class UserInput:
    def __init__(self, input_parser):
        self.input_parser = input_parser

    def take_command(self):
        """
        Takes the user's input, parses it into a recognizable command and returns it
        """
        command = self._parse_input(self._take_input())
        return command

    def _parse_input(self, input):
        return self.input_parser.parse(input)

    def _take_input(self):
        raise NotImplementedError()

class UserMouseInput(UserInput):
    pass

class UserKeyboardInput(UserInput):
    pass

class UserJoystickInput(UserInput):
    pass

這些邊界對(duì)象負(fù)責(zé)向系統(tǒng)內(nèi)部或者外部傳遞信息。例如對(duì)要接收的用戶指令,我們需要一個(gè)邊界對(duì)象來將鍵盤輸入(比如一個(gè)空格鍵)轉(zhuǎn)換為一個(gè)可識(shí)別的域事件(例如角色的跳躍)。

Bonus:值對(duì)象(Value Object)

價(jià)值對(duì)象代表的是域(domain)中的一個(gè)簡單值。它們無法改變,不恒一。

如果將它們結(jié)合在我們的游戲中,Money 類或者 Damage 類就表示這種對(duì)象。上述的對(duì)象讓我們?nèi)菀椎貐^(qū)分、尋找和調(diào)試相關(guān)功能,然而僅使用基礎(chǔ)的整形數(shù)組或者整數(shù)卻無法實(shí)現(xiàn)這些功能。

class Money:
    def __init__(self, gold, silver, copper):
        self.gold = gold
        self.silver = silver
        self.copper = copper

    def __eq__(self, other):
        return self.gold == other.gold and self.silver == other.silver and self.copper == other.copper

    def __gt__(self, other):
        if self.gold == other.gold and self.silver == other.silver:
            return self.copper > other.copper
        if self.gold == other.gold:
            return self.silver > other.silver

        return self.gold > other.gold

    def __add__(self, other):
        return Money(gold=self.gold + other.gold, silver=self.silver + other.silver, copper=self.copper + other.copper)

    def __str__(self):
        return f'Money Object(Gold: {self.gold}; Silver: {self.silver}; Copper: {self.copper})'

    def __repr__(self):
        return self.__str__()


print(Money(1, 1, 1) == Money(1, 1, 1))
# => True
print(Money(1, 1, 1) > Money(1, 2, 1))
# => False
print(Money(1, 1, 0) + Money(1, 1, 1))
# => Money Object(Gold: 2; Silver: 2; Copper: 1)

它們可以歸類為實(shí)體對(duì)象的子類別。

關(guān)鍵設(shè)計(jì)原則

設(shè)計(jì)原則是軟件設(shè)計(jì)中的規(guī)則,過去這些年里已經(jīng)證明它們是有價(jià)值的。嚴(yán)格地遵循這些原則有助于軟件達(dá)到一流的質(zhì)量。

抽象(Abstraction)

抽象就是將一個(gè)概念在一定的語境中簡化為原始本質(zhì)的一種思想。它允許我們拆解一個(gè)概念來更好的理解它。

上面的游戲案例闡述了抽象,讓我們來看一下 Fight 類是如何構(gòu)建的。我們以盡可能簡單的方式使用它,即在實(shí)例化的過程中給它兩個(gè)英雄作為參數(shù),然后調(diào)用 fight() 方法。不多也不少,就這些。

代碼中的抽象過程應(yīng)該遵循最少意外(POLA)的原則,抽象不應(yīng)該用不必要和不相關(guān)的行為/屬性。換句話說,它應(yīng)該是直觀的。

注意,我們的 Hero#take_damage() 函數(shù)不會(huì)做一些異常的事情,例如在還沒死亡的時(shí)候刪除角色。但是如果他的生命值降到零以下,我們可以期望它來殺死我們的角色。

封裝

封裝可以被認(rèn)為是將某些東西放在一個(gè)類以內(nèi),并限制了它向外部展現(xiàn)的信息。在軟件中,限制對(duì)內(nèi)部對(duì)象和屬性的訪問有助于保證數(shù)據(jù)的完整性。

將內(nèi)部編程邏輯封裝成黑盒子,我們的類將更容易管理,因?yàn)槲覀冎滥牟糠挚梢员黄渌到y(tǒng)使用,哪些不行。這意味著我們?cè)诒A艄膊糠植⑶冶WC不破壞任何東西的同時(shí)能夠重用內(nèi)部邏輯。此外,我們從外部使用封裝功能變得更加簡單,因?yàn)樾枰紤]的事情也更少。

在大多數(shù)編程語言中,封裝都是通過所謂的 Access modifiers(訪問控制修飾符)來完成的(例如 private,protected 等等)。Python 并不是這方面的最佳例子,因?yàn)樗荒茉谶\(yùn)行時(shí)構(gòu)建這種顯式修飾符,但是我們使用約定來解決這個(gè)問題。變量和函數(shù)前面的_前綴就意味著它們是私有的。

舉個(gè)例子,試想將我們的 Fight#_run_attack 方法修改為返回一個(gè)布爾變量,這意味著戰(zhàn)斗結(jié)束而不是發(fā)生了意外。我們將會(huì)知道,我們唯一可能破壞的代碼就是 Fight 類的內(nèi)部,因?yàn)槲覀兪前堰@個(gè)函數(shù)設(shè)置為私有的。

請(qǐng)記住,代碼更多的是被修改而不是重寫。能夠盡可能清晰、較小影響的方式修改代碼對(duì)開發(fā)的靈活性很重要。

分解

分解就是把一個(gè)對(duì)象分割為多個(gè)更小的獨(dú)立部分,這些獨(dú)立的部分更易于理解、維護(hù)和編程。

試想我們現(xiàn)在希望 Hero 類能結(jié)合更多的 RPG 特征,例如 buffs,資產(chǎn),裝備,角色屬性。

class Hero:
    def __init__(self, health, mana):
        self._health = health
        self._mana = mana
        self._strength = 0
        self._agility = 0
        self._stamina = 0
        self.level = 0
        self._items = {}
        self._equipment = {}
        self._item_capacity = 30
        self.stamina_buff = None
        self.agility_buff = None
        self.strength_buff = None
        self.buff_duration = -1

    def level_up(self):
        self.level += 1
        self._stamina += 1
        self._agility += 1
        self._strength += 1
        self._health += 5

    def take_buff(self, stamina_increase, strength_increase, agility_increase):
        self.stamina_buff = stamina_increase
        self.agility_buff = agility_increase
        self.strength_buff = strength_increase
        self._stamina += stamina_increase
        self._strength += strength_increase
        self._agility += agility_increase
        self.buff_duration = 10  # rounds

    def pass_round(self):
        if self.buff_duration > 0:
            self.buff_duration -= 1
        if self.buff_duration == 0:  # Remove buff
            self._stamina -= self.stamina_buff
            self._strength -= self.strength_buff
            self._agility -= self.agility_buff
            self._health -= self.stamina_buff * 5
            self.buff_duration = -1
            self.stamina_buff = None
            self.agility_buff = None
            self.strength_buff = None

    def attack(self) -> int:
        """
        Returns the attack damage of the Hero
        """
        return 1 + (self._agility * 0.2) + (self._strength * 0.2)

    def take_damage(self, damage: int):
        self._health -= damage

    def is_alive(self):
        return self._health > 0

    def take_item(self, item: Item):
        if self._item_capacity == 0:
            raise Exception('No more free slots')
        self._items[item.id] = item
        self._item_capacity -= 1

    def equip_item(self, item: Item):
        if item.id not in self._items:
            raise Exception('Item is not present in inventory!')
        self._equipment[item.slot] = item
        self._agility += item.agility
        self._stamina += item.stamina
        self._strength += item.strength
        self._health += item.stamina * 5
# 缺乏分解的案例


我們可能會(huì)說這份代碼已經(jīng)開始變得相當(dāng)混亂了。我們的 Hero 對(duì)象一次性設(shè)置了太多的屬性,結(jié)果導(dǎo)致這份代碼變得相當(dāng)脆弱。

例如,我們的耐力分?jǐn)?shù)為 5 個(gè)生命值,如果將來要修改為 6 個(gè)生命值,我們就要在很多地方修改這個(gè)實(shí)現(xiàn)。

解決方案就是將 Hero 對(duì)象分解為多個(gè)更小的對(duì)象,每個(gè)小對(duì)象可承擔(dān)一些功能。下面展示了一個(gè)邏輯比較清晰的架構(gòu):

如何寫一手漂亮的模型:面向?qū)ο缶幊痰脑O(shè)計(jì)原則綜述

from copy import deepcopy

class AttributeCalculator:
    @staticmethod
    def stamina_to_health(self, stamina):
        return stamina * 6

    @staticmethod
    def agility_to_damage(self, agility):
        return agility * 0.2

    @staticmethod
    def strength_to_damage(self, strength):
        return strength * 0.2

class HeroInventory:
    class FullInventoryException(Exception):
        pass

    def __init__(self, capacity):
        self._equipment = {}
        self._item_capacity = capacity

    def store_item(self, item: Item):
        if self._item_capacity < 0:
            raise self.FullInventoryException()
        self._equipment[item.id] = item
        self._item_capacity -= 1

    def has_item(self, item):
        return item.id in self._equipment

class HeroAttributes:
    def __init__(self, health, mana):
        self.health = health
        self.mana = mana
        self.stamina = 0
        self.strength = 0
        self.agility = 0
        self.damage = 1

    def increase(self, stamina=0, agility=0, strength=0):
        self.stamina += stamina
        self.health += AttributeCalculator.stamina_to_health(stamina)
        self.damage += AttributeCalculator.strength_to_damage(strength) + AttributeCalculator.agility_to_damage(agility)
        self.agility += agility
        self.strength += strength

    def decrease(self, stamina=0, agility=0, strength=0):
        self.stamina -= stamina
        self.health -= AttributeCalculator.stamina_to_health(stamina)
        self.damage -= AttributeCalculator.strength_to_damage(strength) + AttributeCalculator.agility_to_damage(agility)
        self.agility -= agility
        self.strength -= strength

class HeroEquipment:
    def __init__(self, hero_attributes: HeroAttributes):
        self.hero_attributes = hero_attributes
        self._equipment = {}

    def equip_item(self, item):
        self._equipment[item.slot] = item
        self.hero_attributes.increase(stamina=item.stamina, strength=item.strength, agility=item.agility)


class HeroBuff:
    class Expired(Exception):
        pass

    def __init__(self, stamina, strength, agility, round_duration):
        self.attributes = None
        self.stamina = stamina
        self.strength = strength
        self.agility = agility
        self.duration = round_duration

    def with_attributes(self, hero_attributes: HeroAttributes):
        buff = deepcopy(self)
        buff.attributes = hero_attributes
        return buff

    def apply(self):
        if self.attributes is None:
            raise Exception()
        self.attributes.increase(stamina=self.stamina, strength=self.strength, agility=self.agility)

    def deapply(self):
        self.attributes.decrease(stamina=self.stamina, strength=self.strength, agility=self.agility)

    def pass_round(self):
        self.duration -= 0
        if self.has_expired():
            self.deapply()
            raise self.Expired()

    def has_expired(self):
        return self.duration == 0


class Hero:
    def __init__(self, health, mana):
        self.attributes = HeroAttributes(health, mana)
        self.level = 0
        self.inventory = HeroInventory(capacity=30)
        self.equipment = HeroEquipment(self.attributes)
        self.buff = None

    def level_up(self):
        self.level += 1
        self.attributes.increase(1, 1, 1)

    def attack(self) -> int:
        """
        Returns the attack damage of the Hero
        """
        return self.attributes.damage

    def take_damage(self, damage: int):
        self.attributes.health -= damage

    def take_buff(self, buff: HeroBuff):
        self.buff = buff.with_attributes(self.attributes)
        self.buff.apply()

    def pass_round(self):
        if self.buff:
            try:
                self.buff.pass_round()
            except HeroBuff.Expired:
                self.buff = None

    def is_alive(self):
        return self.attributes.health > 0

    def take_item(self, item: Item):
        self.inventory.store_item(item)

    def equip_item(self, item: Item):
        if not self.inventory.has_item(item):
            raise Exception('Item is not present in inventory!')
        self.equipment.equip_item(item)

現(xiàn)在,在將 Hero 對(duì)象分解為 HeroAttributes、HeroInventory、HeroEquipment 和 HeroBuff 對(duì)象之后,未來新增功能就更加容易、更具有封裝性、具有更好的抽象,這份代碼也就越來越清晰了。

下面是三種分解關(guān)系:

  • 關(guān)聯(lián):在兩個(gè)組成部分之間定義一個(gè)松弛的關(guān)系。兩個(gè)組成部分不互相依賴,但是可以一起工作。例如 Hero 對(duì)象和 Zone 對(duì)象。

  • 聚合:在整體和部分之間定義一個(gè)弱「包含」關(guān)系。這種關(guān)系比較弱,因?yàn)椴糠挚梢栽跊]有整體的時(shí)候存在。例如 HeroInventory(英雄財(cái)產(chǎn))和 Item(條目)。HeroInventory 可以有很多 Items,而且一個(gè) Items 也可以屬于任何 HeroInventory(例如交易條目)。

  • 組成:一個(gè)強(qiáng)「包含」關(guān)系,其中整體和部分不能彼此分離。部分不能被共享,因?yàn)檎w要依賴于這些特定的部分。例如 Hero(英雄)和 HeroAttributes(英雄屬性)。

泛化

泛化可能是最重要的設(shè)計(jì)原則,即我們提取共享特征,并將它們結(jié)合到一起的過程。我們都知道函數(shù)和類的繼承,這就是一種泛化。

做一個(gè)比較可能會(huì)將這個(gè)解釋得更加清楚:盡管抽象通過隱藏非必需的細(xì)節(jié)減少了復(fù)雜性,但是泛化通過用一個(gè)單獨(dú)構(gòu)造體來替代多個(gè)執(zhí)行類似功能的實(shí)體。

 Two methods which share common characteristics
def take_physical_damage(self, physical_damage):
    print(f'Took {physical_damage} physical damage')
    self._health -= physical_damage

def take_spell_damage(self, spell_damage):
    print(f'Took {spell_damage} spell damage')
    self._health -= spell_damage

# vs.

# One generalized method
def take_damage(self, damage, is_physical=True):
    damage_type = 'physical' if is_physical else 'spell'
    print(f'Took {damage} {damage_type} damage')
    self._health -= damage

以上是函數(shù)示例,這種方法缺少泛化性能,而下面展示了具有泛化性能的案例。

class Entity:
    def __init__(self):
        raise Exception('Should not be initialized directly!')

    def attack(self) -> int:
        """
        Returns the attack damage of the Hero
        """
        return self.attributes.damage

    def take_damage(self, damage: int):
        self.attributes.health -= damage

    def is_alive(self):
        return self.attributes.health > 0


class Hero(Entity):
    pass

class NPC(Entity):
    pass

在給出的例子中,我們將常用的 Hero 類和 NPC 類泛化為一個(gè)共同的父類 Entity,并通過繼承簡化子類的構(gòu)建。

這里,我們通過將它們的共同功能移動(dòng)到基本類中來減少復(fù)雜性,而不是讓 NPC 類和 Hero 類將所有的功能都實(shí)現(xiàn)兩次。

我們可能會(huì)過度使用繼承,因此很多有經(jīng)驗(yàn)的人都建議我們更偏向使用組合(Composition)而不是繼承(https://stackoverflow.com/a/53354)。

繼承常常被沒有經(jīng)驗(yàn)的程序員濫用,這可能是由于繼承是他們首先掌握的 OOP 技術(shù)。

組合

組合就是把多個(gè)對(duì)象結(jié)合為一個(gè)更復(fù)雜對(duì)象的過程。這種方法會(huì)創(chuàng)建對(duì)象的示例,并且使用它們的功能,而不是直接繼承它。

使用組合原則的對(duì)象就被稱作組合對(duì)象(composite object)。這種組合對(duì)象在要比所有組成部分都簡單,這是非常重要的一點(diǎn)。當(dāng)把多個(gè)類結(jié)合成一個(gè)類的時(shí)候,我們希望把抽象的層次提高一些,讓對(duì)象更加簡單。

組合對(duì)象的 API 必須隱藏它的內(nèi)部模塊,以及內(nèi)部模塊之間的交互。就像一個(gè)機(jī)械時(shí)鐘,它有三個(gè)展示時(shí)間的指針,以及一個(gè)設(shè)置時(shí)間的旋鈕,但是它內(nèi)部包含很多運(yùn)動(dòng)的獨(dú)立部件。

正如我所說的,組合要優(yōu)于繼承,這意味著我們應(yīng)該努力將共用功能移動(dòng)到一個(gè)獨(dú)立的對(duì)象中,然后其它類就使用這個(gè)對(duì)象的功能,而不是將它隱藏在所繼承的基本類中。

讓我們闡述一下過度使用繼承功能的一個(gè)可能會(huì)發(fā)生的問題,現(xiàn)在我們僅僅向游戲中增加一個(gè)行動(dòng):

class Entity:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        raise Exception('Should not be initialized directly!')

    def attack(self) -> int:
        """
        Returns the attack damage of the Hero
        """
        return self.attributes.damage

    def take_damage(self, damage: int):
        self.attributes.health -= damage

    def is_alive(self):
        return self.attributes.health > 0

    def move_left(self):
        self.x -= 1

    def move_right(self):
        self.x += 1


class Hero(Entity):
    pass

class NPC(Entity):
    pass

正如我們所學(xué)到的,我們將 move_right 和 move_left 移動(dòng)到 Entity 類中,而不是直接復(fù)制代碼。

好了,如果我們想在游戲中引入坐騎呢?坐騎也應(yīng)該需要左右移動(dòng),但是它沒有攻擊的能力,甚至沒有生命值。

我們的解決方案可能是簡單地將 move 邏輯移動(dòng)到獨(dú)立的 MoveableEntity 或者 MoveableObject 類中,這種類僅僅含有那項(xiàng)功能。

那么,如果我們想讓坐騎具有生命值,但是無法攻擊,那該怎么辦呢?希望你可以看到類的層次結(jié)構(gòu)是如何變得復(fù)雜的,即使我們的業(yè)務(wù)邏輯還是相當(dāng)簡單。

一個(gè)從某種程度來說比較好的方法是將動(dòng)作邏輯抽象為 Movement 類(或者其他更好的名字),并且在可能需要的類里面把它實(shí)例化。這將會(huì)很好地封裝函數(shù),并使其在所有種類的對(duì)象中都可以重用,而不僅僅局限于實(shí)體類。

批判性思考

盡管這些設(shè)計(jì)原則是在數(shù)十年經(jīng)驗(yàn)中形成的,但盲目地將這些原則應(yīng)用到代碼之前進(jìn)行批判性思考是很重要的。

任何事情都是過猶不及!有時(shí)候這些原則可以走得很遠(yuǎn),但是實(shí)際上有時(shí)會(huì)變成一些很難使用的東西。

作為一個(gè)工程師,我們需要根據(jù)獨(dú)特的情境去批判地評(píng)價(jià)最好的方法,而不是盲目地遵從并應(yīng)用任意的原則。

關(guān)注點(diǎn)的內(nèi)聚、耦合和分離

內(nèi)聚(Cohesion)

內(nèi)聚代表的是模塊內(nèi)部責(zé)任的分明,或者是模塊的復(fù)雜度。

如果我們的類只執(zhí)行一個(gè)任務(wù),而沒有其它明確的目標(biāo),那么這個(gè)類就有著高度內(nèi)聚性。另一方面,如果從某種程度而言它在做的事情并不清楚,或者具有多于一個(gè)的目標(biāo),那么它的內(nèi)聚性就非常低。

我們希望代碼具有較高的內(nèi)聚性,如果發(fā)現(xiàn)它們有非常多的目標(biāo),或許我們應(yīng)該將它們分割出來。

耦合

耦合獲取的是連接不同類的復(fù)雜度。我們希望類與其它的類具有盡可能少、盡可能簡單的聯(lián)系,所以我們就可以在未來的事件中交換它們(例如改變網(wǎng)絡(luò)框架)。

在很多編程語言中,這都是通過大量使用接口來實(shí)現(xiàn)的,它們抽象出處理特定邏輯的類,然后表征為一種適配層,每個(gè)類都可以嵌入其中。

分離關(guān)注點(diǎn)

分離關(guān)注點(diǎn)(SoC)是這樣一種思想:軟件系統(tǒng)必須被分割為功能上互不重疊的部分。或者說關(guān)注點(diǎn)必須分布在不同的地方,其中關(guān)注點(diǎn)表示能夠?yàn)橐粋(gè)問題提供解決方案。

網(wǎng)頁就是一個(gè)很好的例子,它具有三個(gè)層(信息層、表示層和行為層),這三個(gè)層被分為三個(gè)不同的地方(分別是 HTML,CSS,以及 JS)。

如果重新回顧一下我們的 RPG 例子,你會(huì)發(fā)現(xiàn)它在最開始具有很多關(guān)注點(diǎn)(應(yīng)用 buffs 來計(jì)算襲擊傷害、處理資產(chǎn)、裝備條目,以及管理屬性)。我們通過分解將那些關(guān)注點(diǎn)分割成更多的內(nèi)聚類,它們抽象并封裝了它們的細(xì)節(jié)。我們的 Hero 類現(xiàn)在僅僅作為一個(gè)組合對(duì)象,它比之前更加簡單。

結(jié)語

對(duì)小規(guī)模的代碼應(yīng)用這些原則可能看起來很復(fù)雜。但是事實(shí)上,對(duì)于未來想要開發(fā)和維護(hù)的任何一個(gè)軟件項(xiàng)目而言,這些規(guī)則都是必須的。在剛開始寫這種代碼會(huì)有些成本,但是從長期來看,它會(huì)回報(bào)以幾倍增長。

這些原則保證我們的系統(tǒng)更加:

  • 可擴(kuò)展:高內(nèi)聚使得不用關(guān)心不相關(guān)的功能就可以更容易地實(shí)現(xiàn)新模塊。

  • 可維護(hù):低耦合保證一個(gè)模塊的改變通常不會(huì)影響其它模塊。高內(nèi)聚保證一個(gè)系統(tǒng)需求的改變只需要更改盡可能少的類。

  • 可重用:高內(nèi)聚保證一個(gè)模塊的功能是完整的,也是被妥善定義的。低耦合使得模塊盡可能少地依賴系統(tǒng)的其它部分,這使得模塊在其它軟件中的重用變得更加容易。

在本文中,我們首先介紹了一些高級(jí)對(duì)象的類別(實(shí)體對(duì)象、邊界對(duì)象以及控制對(duì)象)。然后我們了解了一些構(gòu)建對(duì)象時(shí)使用的關(guān)鍵原則,比如抽象、泛化、分解和封裝等。最后,我們引入了兩個(gè)軟件質(zhì)量指標(biāo)(耦合和內(nèi)聚),然后學(xué)習(xí)了使用這些原則能夠帶來的好處。

我希望這篇文章提供了一些關(guān)于設(shè)計(jì)原則的概覽,如果我們希望自己能夠在這個(gè)領(lǐng)域獲得更多的進(jìn)步,我們還需要了解更多具體的操作。

來自: https://www.jiqizhixin.com/articles/050101

標(biāo)簽: 代碼 互聯(lián)網(wǎng) 數(shù)據(jù)庫 網(wǎng)絡(luò)

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:你想知道的2018年軟件開發(fā)“10件事”

下一篇:幾種開源的媒體服務(wù)器對(duì)比