剛結束Coding Style,今天介紹軟體工程的觀念OOP和SOLID
這兩個觀念都是程式設計風格、原則或者說是典範
當然不限用於PHP或者Laravel
OOP和SOLID部分方面很像
它們的目的都是強調程式的重用性、可讀性、可測試性、可維護性、可擴展性,形成高內聚、低耦合
OOP,(Object-oriented programming,物件導向程式設計),是種具有物件概念的程式設計典範,同時也是一種程式開發的抽象方針。它可能包含資料、特性、程式碼與方法。物件則指的是類別(class)的實例。
記得在學期間學到OOP一句話讓筆者印象深刻
在OOP的世界中,每一項事物都是一個物件,萬物皆物件
舉例來說,今天要定義一類動物,
此類動物具有頭、足、髮色等
並可以 爬、睡覺、吃
如果寫入PHP大概會是:
class Animal
{
public $head;
public $legs;
public $hairColor;
public function climb()
{
echo 'climb';
}
public function sleep()
{
echo 'sleep';
}
public function eat()
{
echo 'eat';
}
}
其中public(公有)依需求可以寫作protected(保護)、private(私有),
這牽扯到繼承問題,之後會介紹,有興趣也可以自己深入~
而今天要實例化這個類別:
use Animal;
class PostController
{
public function index()
{
$animal = new Animal();
$animal->climb();
}
}
被實例化後的類(Animal),可以被視為一物件($animal)
以上大致上就是OOP的簡易範例
SOLID的觀念更加複雜
以上可能有點抽象,筆者試著用Animal舉正例及反例,分別解釋五個原則的基本概念,如果有誤麻煩留言告知我orz
正例: Animal僅有它具有的屬性及功能,如:頭、吃
反例: Animal新增一功能是付款,這並不符合動物的功能
符合 OCP 的示例:
使用繼承和多態性:假設您有一個 Animal 基類別,並且派生了具體的動物類別,如 Cat 和 Dog。每個具體的動物類別可以擴展其行為,而不需要修改 Animal 基類別。這符合 OCP,因為您可以輕鬆地添加新的動物類別而無需修改現有的程式碼。
class Animal {
// 基本屬性和方法
}
class Cat extends Animal {
public function makeSound() {
echo 'Meow';
}
}
class Dog extends Animal {
public function makeSound() {
echo 'Woof';
}
}
使用介面:如果您定義一個介面(例如 AnimalInterface)來表示所有動物都應該具有的方法,然後您的具體動物類別實現這個介面,這也符合 OCP。您可以輕鬆地添加新的動物類別,只需確保它實現了介面中的方法。
interface AnimalInterface {
public function makeSound();
}
class Cat implements AnimalInterface {
public function makeSound() {
echo 'Meow';
}
}
class Dog implements AnimalInterface {
public function makeSound() {
echo 'Woof';
}
}
違反 OCP 的示例:
直接修改基類別:如果您需要添加新的行為或修改現有的行為,但直接修改 Animal 基類別的程式碼,這就違反了 OCP。這會影響到現有的程式碼,可能會引入錯誤。
class Animal {
public function makeSound() {
echo 'Generic animal sound';
}
}
// 違反 OCP:直接修改基類別
class Cat extends Animal {
public function makeSound() {
echo 'Meow';
}
}
條件式邏輯:如果您在程式碼中使用大量的條件式邏輯,以根據不同的情況執行不同的行為,這也可能違反 OCP。當您需要添加新的情況時,您必須修改現有的程式碼,這不是對修改封閉的。
class Animal {
public function makeSound($type) {
if ($type === 'cat') {
echo 'Meow';
} elseif ($type === 'dog') {
echo 'Woof';
} else {
echo 'Generic animal sound';
}
}
}
總之,OCP 讓你可以擴展功能而無需修改現有的程式碼,並且可以通過繼承、介面、多態性等方式實現。
長方形與正方形的例子
長方形:
class Rectangle{
public width;
public height;
public function setWidth($w){
$this.width = w;
}
public function setHeight($h){
$this.height = h;
}
public function getArea(){
return $this->width * $this.height;
}
}
正方形:
class Square extends Rectangle{
public function setWidth($w){
$this.width = w;
$this.height = h;
}
public function setHeight($h){
$this.width = w;
$this.height = h;
}
public function getArea(){
return $this->width * $this.height;
}
}
use Rectangle;
class PostController
{
public function index()
{
$rectangle = new Rectangle();
$rectangle->setWidth(20);
$rectangle->setWidth(10);
$rectangle->getArea(); // 200
}
}
1.Square不僅違反OCP(因為它重寫setWidth、setHeight)
2.也違反里氏替換原則(如果把Rectangle替換成Square,得出來的結果會是100)
介面隔離原則強調介面不應被迫使用對其而言無用的方法或功能,盡可能的將臃腫龐大的interface拆分成小而具體
以汽車為例:
interface Hybrid
{
public $bigBattery;
public $tank;
public function fastCharge();
public function burningFuel();
}
class HybridCar implements Hybrid
{
}
以上是不好的示範:
以下則有符合介面隔離原則
interface Electronic
{
public $bigBattery;
public function fastCharge();
}
interface Fuel
{
public $tank;
public function burningFuel();
}
class HybridCar implements Electronic, Fuel
{
}
class FuelCar implements Fuel
{
}
依賴反轉的理念在於分解高層的組件對低組件的依賴,這時候有個關鍵:介面(interface)
維基有提供一個例子是
在此例子中,檯燈是低層組件、按鈕則是高層
檯燈中定義一介面(定義了開關),解開了檯燈與按鈕的耦合性
不符合依賴反轉原則:
use UserService;
class Post
{
public function index(){
$userService = new UserService();// 直接實例化低層次模組
$user = $userService->find(1);
}
}
符合:
use UserService;
class Post
{
protected $userService;
public function __construct(UserService $userService) {
$this->userService = $userService;
}
public function index(){
// 使用注入的 userService 依賴進行查詢
$user = $this->userService->find(1)
}
}
下一篇會利用Laravel的Service Container、Service Provider在深入依賴注入
筆者在之前也不太清楚SOLID的詳細定義(現在也不清楚XD
這篇帶著讀者以最簡單的例子了解到SOLID的定義及重要性
不知道讀者是否和本人有同樣的疑問
分這麼細是否有效率問題
以下是個人見解:
前面提到這些設計風格是讓程式增加重用性、可讀性、可測試性、可維護性、可擴展性,形成高內聚、低耦合
就像你拿原生PHP和Laravel比效率,肯定是原生比較快
但是所帶來的開發時長、維護等等可能超乎預期
同時開發者在開發時為了快速而造成程式碼難以維護及重用,也是一項重大課題
物件導向程式設計 - Wiki
深入淺出 Liskov 替換原則 Liskov Substitution Principle
SOLID (物件導向設計)