# 物件導向
### 請問物件導向程式語言有哪些特點,能簡單說明給我聽嗎?
物件導向程式語言 (Object-Oriented Programming, OOP) 的特點有:
1. 物件導向:OOP 程式語言是以物件 (Object) 為基礎建構程式,物件具有狀態 (state) 和行為 (behavior)。
2. 繼承 (Inheritance):OOP 程式語言允許一個類別 (class) 從另一個類別繼承屬性和行為。
3. 多型 (Polymorphism):OOP 程式語言允許物件在不同的情況下具有不同的行為。
4. 封裝 (Encapsulation):OOP 程式語言允許將物件的狀態和行為封裝在一起,保護物件內部狀態不被外界直接存取。
5. 抽象 (Abstraction) ,OOP 程式語言將物件的實作細節與使用者分開,使用者只需要知道物件的公開介面而無需知道其實作。
### 請問物件具有狀態 (state) 和行為 (behavior)。這句話是什麼意思?能用python舉例嗎?
物件具有狀態 (state) 和行為 (behavior) 意思是物件具有兩種不同的特性,狀態代表物件的屬性,行為代表物件的方法。
舉例來說,若我們有一個類別叫做 "Car",那麼這個類別可能有一些狀態屬性如 "color"、"speed"、"brand",而行為可能有 "drive"、"brake"、"turn" 等方法。
用python 程式碼來實現這個類別可能長得像下面這樣:
```python
class Car:
def __init__(self, color, speed, brand):
self.color = color
self.speed = speed
self.brand = brand
def drive(self):
self.speed += 10
def brake(self):
self.speed -= 10
def turn(self, direction):
print(f"The car is turning {direction}")
my_car = Car("red", 0, "Tesla")
my_car.drive()
my_car.turn("left")
print(my_car.speed) # 10
```
上面這個例子中,類別 Car 有三個狀態屬性 color, speed, brand 以及三個行為方法 drive(), brake(), turn()。
透過創建一個實體物件 my_car = Car("red", 0, "Tesla") 來使用這些方法及屬性。
### 請問你能用上面的Car class寫一個繼承的範例給我看嗎?
當然可以,我們可以創建一個新的類別來繼承 Car 類別,讓新類別繼承 Car 類別的屬性和方法。
例如我們可以創建一個新的類別 "ElectricCar" 繼承 Car 類別,並且在 ElectricCar 類別中加入新的屬性和方法。
```python
class ElectricCar(Car):
def __init__(self, color, speed, brand, battery_size):
super().__init__(color, speed, brand)
self.battery_size = battery_size
def charge(self):
print("The electric car is charging...")
def drive(self):
if self.battery_size > 0:
self.speed += 20
self.battery_size -= 10
else:
print("The battery is empty, please charge the car.")
my_electric_car = ElectricCar("blue", 0, "Tesla", 100)
my_electric_car.drive()
my_electric_car.drive()
my_electric_car.drive()
print(my_electric_car.speed) # 60
print(my_electric_car.battery_size) # 70
```
在這個例子中, ElectricCar 類別繼承了 Car 類別的所有屬性和方法,並且定義了新的屬性 battery_size 和方法 charge()。
透過使用 super() 函式可以繼承 Car類別的 init 方法。
使用 my_electric_car = ElectricCar("blue", 0, "Tesla", 100) 來創建實體物件,可以使用 ElectricCar 類別的新屬性及方法。
### 請問你能用上面的程式碼繼續舉例多型的用法給我看嗎?
當然可以,多型 (Polymorphism) 是指物件在不同的情況下具有不同的行續。 一般來說我們可以使用繼承的類別來實現多型。
以上面程式碼為例,我們可以用 ElectricCar 類別繼承 Car 類別,並重新實現 drive 方法來實現多型的用法。
```python
class ElectricCar(Car):
def __init__(self, color, speed, brand, battery_size):
super().__init__(color, speed, brand)
self.battery_size = battery_size
def charge(self):
print("The electric car is charging...")
def drive(self):
if self.battery_size > 0:
self.speed += 20
self.battery_size -= 10
else:
print("The battery is empty, please charge the car.")
class GasCar(Car):
def __init__(self, color, speed, brand, gas_level):
super().__init__(color, speed, brand)
self.gas_level = gas_level
def fill_gas(self):
self.gas_level = 100
def drive(self):
if self.gas_level > 0:
self.speed += 15
self.gas_level -= 10
else:
print("The gas is empty, please fill the gas.")
my_electric_car = ElectricCar("blue", 0, "Tesla", 100)
my_gas_car = GasCar("black", 0, "Ford", 60)
def drive_car(car):
car.drive()
drive_car(my_electric_car)
drive_car(my_gas_car)
print(my_electric_car.speed) # 20
print(my_gas_car.speed) # 15
```
在這個例子中,我們在 ElectricCar 和 GasCar 類別中都重新實現了 drive 方法,並且在 drive_car 函數中只接受 Car 的類別,可以看到在 drive_car 函數中傳入不同類別的實體物件會執行不同的drive行為。
這就是多型的用法,可以讓同一個函數或方法支援多種類別的物件,而不需要為每一種類別都寫一個不同的函數或方法。這樣可以增加程式碼的彈性和可讀性。
我們可以在設計類別時統一實現某些方法,並讓類別可以自行選擇如何實現該方法,這樣就可以讓程式碼更具彈性和靈活性。
### 請問你能用上面的程式碼繼續舉例封裝的用法給我看嗎?
當然可以,封裝 (Encapsulation) 是指將物件的狀態和行為封裝在一起,保護物件內部狀態不被外界直接存取。
在 Python 中,我們可以使用 _ 或 __ 來表示私有變數和方法,這樣的變數和方法只能在類別內部存取。
例如,我們可以把 ElectricCar 類別中的 battery_size 屬性改成私有變數:
```python
class ElectricCar(Car):
def __init__(self, color, speed, brand, battery_size):
super().__init__(color, speed, brand)
self.__battery_size = battery_size
def charge(self):
print("The electric car is charging...")
def drive(self):
if self.__battery_size > 0:
self.speed += 20
self.__battery_size -= 10
else:
print("The battery is empty, please charge the car.")
def get_battery_size(self):
return self.__battery_size
my_electric_car = ElectricCar("blue", 0, "Tesla", 100)
print(my_electric_car.get_battery_size()) #100
```
在這個例子中, ElectricCar 類別的 battery_size 屬性被定義為私有變數,只能在類別內部存取。如果外部直接存取該變數的話會產生錯誤。
而我們可以使用私有變數所對應的公有方法 get_battery_size() 來存取私有變數。這樣可以保護類別內部狀態不被外部直接存取,也可以通過公有方法給外部提供適當的存取權限。
### 你說在 Python 中,我們可以使用 _ 或 __ 來表示私有變數和方法,請問_ 跟__ 差在哪裡?
用一個底線 (_) 表示私有變數和方法並不會真正的阻止外部存取,只是約定俗成的寫法。
而使用兩個底線 (__) 來表示私有變數和方法會真正的封裝,阻止外部存取,並且子類別也無法存取,更能保證封裝性。
### 請問你能用上面的程式碼繼續舉例抽象的用法給我看嗎?
可以,以下是之前提到的 Car、ElectricCar、GasCar 程式碼使用抽象類別和抽象方法的寫法:
```python
from abc import ABC, abstractmethod
class Car(ABC):
def __init__(self):
self.__year_model = 0
self.__make = ""
self.__speed = 0
@abstractmethod
def drive(self):
pass
class ElectricCar(Car):
def __init__(self):
super().__init__()
self.__battery_type = ""
def set_battery_type(self, battery_type):
self.__battery_type = battery_type
def get_battery_type(self):
return self.__battery_type
def drive(self):
print("Electric car is driving.")
class GasCar(Car):
def __init__(self):
super().__init__()
self.__fuel_type = ""
def set_fuel_type(self, fuel_type):
self.__fuel_type = fuel_type
def get_fuel_type(self):
return self.__fuel_type
def drive(self):
print("Gas car is driving.")
#create object
car = ElectricCar()
car.drive()
car = GasCar()
car.drive()
```
在這個範例中,Car類別是一個抽象類別,它定義了一個抽象方法 drive()。 ElectricCar 和 GasCar 類別都繼承自 Car 類別,並實現了 drive() 方法。
這樣的設計可以達到封裝的目的,同時也可以達到多型的目的。 並且因為Car類別是抽象類別,所以不能直接建立Car類別的物件,只能用它的子類別來建立物件。
### 有了繼承的特點為何還需要抽象?感覺不出這兩者有何差別
繼承的作用是讓子類別可以繼承父類別的屬性和方法,使得子類別可以共用父類別的程式碼,達到代碼重用的目的。這樣可以讓程式碼更簡潔,並且可以讓子類別擁有父類別的特徵。
而抽象則是讓類別有共同的特徵和行為,抽象類別不能被實例化,而是用來被其他類別繼承。抽象類別中可以定義抽象方法(abstract method)和普通方法,抽象方法只有定義,沒有實現。在子類別中必須實現抽象方法。這樣可以確保所有子類別都有相同的行為,但實現可能不同。
在車的例子中,我們可以用繼承的方式,讓ElectricCar和GasCar繼承Car的屬性,例如make, model, year_model。而用抽象的方式,我們可以在父類別Car中定義一個drive方法,並在子類別中實現drive方法,這樣可以確保所有車子都有drive這個行為。
### 實際業務上來說,何時會想用繼承何時會想用抽象?
實際業務上,繼承和抽象會根據實際需求而使用。
- 繼承:
要繼承另一個類別的特徵和行為時,就可以使用繼承。例如,你有一個車類別,並且你需要創建一個電動車和汽油車類別,這兩個類別都有許多相同的特徵和行為,那麼你可以使用繼承,將車類別的特徵和行為繼承到電動車和汽油車類別中。
- 抽象
當你有一些類別需要共用同一個介面或行為時,就可以使用抽象。例如,你有一個車類別,並且你需要創建多種不同類型的車,例如電動車、汽油車、柴油車等,這些車都需要有 drive 這個行為,那麼你可以使用抽象類別來定義一個 drive 方法,並在電動車、汽油車、柴油車等類別中實現 drive 方法,這樣就可以確保所有車都有 drive 這個行為。
總結來說,繼承是用來繼承父類別的特徵和行為,而抽象則是用來確保類別具有共同的特徵和行為。
### 請問你說的特徵具體來說是指什麼東西?
特徵是指類別所具有的屬性和行為,包括資料和函式。
屬性是指類別中的變數,它們用來儲存類別中的資料。例如在之前的例子中,Car類別中的屬性有 make, model, year_model。
而行為是指類別中的函式,它們用來定義類別的動作。例如在之前的例子中,Car類別中的行為有 drive, get_make, get_model等。
這些屬性和行為就是類別的特徵。
### 請問物件導向為何叫做物件導向?
物件導向程式語言之所以叫做物件導向是因為它重視物件 (Object) 這個概念。在物件導向程式語言中,程式設計師可以使用類別 (Class) 來定義物件的共性,並使用物件來實例化類別。
物件具有狀態 (state) 和行為 (behavior) ,並且可以互相之間進行溝通和互動。透過物件導向程式設計,可以更加形象地模擬實際世界中的事物,並且更加容易維護和擴展程式碼。