Different shaped objects passing through same shaped hole, representing polymorphism in programming

物件導向編程 (Object-Oriented Programming)(019)-多型、抽象、封裝應用與解析

在前一篇文章中,我們已經認識了物件導向編程(OOP)的基礎,並深入學習了 繼承 (Inheritance)

繼承幫助我們重用程式碼並建立層次結構,除了繼承之外,OOP 還有三個同樣重要的特性:

  • 多型 (Polymorphism):相同的操作,能夠根據不同物件而展現不同的行為。
  • 抽象 (Abstraction):隱藏細節,強調必要的行為,幫助我們建立清晰的設計契約。
  • 封裝 (Encapsulation):將資料與方法打包,保護內部細節,僅提供必要的接口給外部使用。

這三個特性讓程式碼更靈活、更安全、更容易維護。

多型 (Polymorphism)

多型是物件導向編程的另一個重要支柱,它允許我們對不同類型的物件執行相同的操作。

在Python中,多型可以通過多種方式實現:

  1. 方法覆寫:不同類別可以有同名但行為不同的方法
  2. 運算符重載:同一個運算符可以對不同類型的物件執行不同的操作

多型的一個主要優點是它允許我們編寫更通用、更靈活的代碼,不必關心物件的具體類型。

Different shaped objects passing through same shaped hole, representing polymorphism in programming

多型的例子

不同類別的相同方法名

class Animal:
  def __init__(self, name):
    self.name = name
  def make_noise(self):
    print("{} says, Grrrr".format(self.name))

class Cat(Animal):
  def make_noise(self):
    print("{} says, Meow!".format(self.name))

class Robot:
  def make_noise(self):
    print("beep.boop...BEEEEP!!!")

使用多型

an_animal = Animal("Bear")
my_pet = Cat("Maisy")
my_vacuum = Robot()

objects = [an_animal, my_pet, my_vacuum]

for o in objects:
  o.make_noise()
  
# 輸出:
# "Bear says, Grrrr"
# "Maisy says, Meow!"
# "beep.boop...BEEEEP!!!"

在這個例子中,我們可以調用不同物件的make_noise()方法,而不需要知道它們的具體類型。這就是多型的力量!

Dunder方法和運算符重載

Dunder方法(雙下劃線方法)是Python中的特殊方法,允許我們自定義類別的行為。例如:

  • __init__(): 構造函數
  • __repr__(): 字符串表示
  • __add__(): 加法運算符
  • __len__(): 長度函數

通過定義這些方法,我們可以實現運算符重載,使運算符對不同類型的物件有不同的行為。

class Animal:
  def __init__(self, name):
    self.name = name
    
  def __repr__(self):
    return self.name
    
  def __add__(self, another_animal):
    return Animal(self.name + another_animal.name)

a1 = Animal("Horse")
a2 = Animal("Penguin")
a3 = a1 + a2

print(a1)  # 輸出: Horse
print(a2)  # 輸出: Penguin
print(a3)  # 輸出: HorsePenguin

在這個例子中,我們定義了__add__()方法,使得可以使用+運算符將兩個Animal物件”加”在一起,創建一個新的Animal物件,其名字是兩個原始物件名字的組合。

抽象 (Abstraction)

抽象是物件導向編程的第三個支柱,它幫助我們隱藏複雜性,只展示必要的部分。

在Python中,我們可以使用抽象基類(Abstract Base Classes,ABC)來實現抽象。抽象類不能被實例化,它們只是作為其他類別的模板。

from abc import ABC, abstractmethod

class Animal(ABC):
  def __init__(self, name):
    self.name = name
    
  @abstractmethod
  def make_noise(self):
    pass

class Cat(Animal):
  def make_noise(self):
    print("{} says, Meow!".format(self.name))

在這個例子中,Animal是一個抽象類,它定義了所有動物應該有的基本行為(make_noise),但沒有實現它。子類別(如Cat)必須實現這個方法,否則會出錯。

Abstract representation of a blueprint or template with details hidden

抽象的優勢

強制一致性

抽象類可以確保所有子類別實現特定的方法,保持API的一致性。在我們的例子中,所有Animal的子類別都必須實現make_noise()方法。

定義契約

抽象類定義了一個”契約”,規定了子類別必須遵循的行為。這有助於確保代碼的正確性和可靠性。

提高設計質量

抽象鼓勵我們思考類別的核心行為和責任,而不是具體的實現細節。這有助於創建更好的類別層次結構。

抽象是一個強大的工具,可以幫助我們設計更加模組化、可維護的程式。通過定義抽象類別,我們可以確保類別層次結構的一致性和正確性。

封裝 (Encapsulation)

封裝是物件導向編程的第四個支柱,它涉及將數據和方法打包成一個單元,並限制對內部細節的訪問。

在許多語言中,有三種訪問修飾符:

  • 公共(Public):可以從任何地方訪問
  • 受保護(Protected):只能從同一模塊訪問
  • 私有(Private):只能從定義它們的類別內部訪問

Python沒有嚴格的訪問控制,但有一些約定:

  • 單下劃線(_x):表示受保護的成員
  • 雙下劃線(__x):表示私有成員(名稱會被修改)

封裝幫助我們隱藏實現細節,只暴露必要的接口。這樣,我們可以在不影響用戶代碼的情況下更改內部實現。

A locked box with some visible and some hidden components, representing encapsulation in programming

Getter、Setter和Deleter

Getter、Setter和Deleter是實現封裝的一種方式,它們允許我們控制對類別屬性的訪問和修改。

class Animal:
  def __init__(self, name):
    self._name = name
    self._age = None
    
  def get_age(self):
    return self._age
    
  def set_age(self, new_age):
    if isinstance(new_age, int):
      self._age = new_age
    else:
      raise TypeError
      
  def delete_age(self):
    print("_age Deleted")
    del self._age

在這個例子中,我們使用getter(get_age)、setter(set_age)和deleter(delete_age)來控制對_age屬性的訪問、修改和刪除。setter還包含驗證邏輯,確保年齡只能是整數。

結語

至此,我們已經完整認識了 OOP 的四大支柱:繼承、多型、抽象、封裝
掌握這些概念後,你不僅能寫出可以運行的程式,更能設計出 模組化、可維護、可擴展 的架構。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

High Quality

Lorem ipsum dolor sit amet, consectetur adipiscing elitsed do eiusmod tempor.

Fast Delivery

Lorem ipsum dolor sit amet, consectetur adipiscing elitsed do eiusmod tempor.

Best Warranty

Lorem ipsum dolor sit amet, consectetur adipiscing elitsed do eiusmod tempor.