大家好:
目前在學習物件導向設計
想請問對於類別與函數的單一職責,它們的範圍要怎麼決定呢?
例如類別的職責:
我有一個 Data 類別負責儲存從資料庫查詢到的數據
當要將這些數據用表格顯示在網頁上時
想請問:
Data 類別可以設定一個 display() 方法來負責這項任務嗎?
還是要另外建立一個 Display 類別來負責顯示數據在網頁上?
又例如函數的職責:
我要將使用者從表單送出的數據,用 create() 方法新增到資料庫內
而在新增數據之前,我要用 filter() 方法先過濾這些數據
確認是正確數據後才新增到資料庫
想請問:
filter() 方法是要放在 create() 方法內?
還是先執行 filter() 方法,確認數據沒問題後再執行 create() 方法呢?
我的想法是:
因為要新增數據到資料庫內之前,每次都一定要先確認數據沒問題,這二個任務是不可分割的
所以過濾數據應該要放在 create() 方法內
但這樣 create() 方法好像就不是只有單一職責了
對於類別和函數的單一職責的範圍,一直有疑問
想請教大家
謝謝
我覺得把握一個大原則,當你覺得code重複時代表你的code不好。
就以上你自己舉的兩個例子:
1.假設你今天有個Person類別、另一個是Order類別,存的欄位不一樣,都是你自己可以掌控/修改的,你都想把這兩個類別產生的實例下面的屬性都列舉出來,請問你是會兩個類別都用個別的display方法,還是利用一個display函式去做?
2.你要create之前要filter,那有時候你不用filter就create,有時候你要filter的時候還要做某些特殊動作(比如傳callback之類的),那你難道對那些特例還要做特殊處理嗎?
這兩個例子我會給的python虛擬碼會是下面這樣:
1.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Order:
def __init__(self, price, date):
self.price = price
self.date = date
def display(obj):
atts = [print(att, getattr(obj, att)) for att in dir(obj) if not att.startswith("_")] #顯示所有公開屬性
p = Person("John", 32)
o = Order(1000, '2019-02-01')
display(p)
display(o)
而不是:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def display(self):
print(self.name)
print(self.age)
class Order:
def __init__(self, price, date):
self.price = price
self.date = date
def display(self):
print(self.price)
print(self.date)
def filter():
...
def create():
...
data1 = filter(ori_data, column1='aaa', column2='bbb', ...)
create(data1)
而不是
def create(data):
def filter(data):
...
tmp = filter(data)
...
當然你在class內可以這樣:
class ex1:
def filter(self):
...
def create(self):
tmp = self.filter(...)
...
個人淺見,你可以參考參考,不一定要全盤接收
依單一職責的原理,設計該類別時要有「針對性」且「複雜度低」又要易於「維護」的大範圍區段
一開始在學的時候有時候會拆的太過或是綁的太死,現在也是還在磨練中
我自己是有一個判斷方式
就是先用行為判別,行為判別的定義在於此類別的「可視範圍」到哪跟我有另外一個任務要加到已存在的類別時的「否定再延伸」
以你的data class來說
定義可視範圍:「負責儲存從資料庫查詢到的數據」在這一段話裡分析出
可視範圍:
存取
--儲存
----新增
----修改
----刪除
--取用
----查詢
----過濾
否定再延伸「負責顯示數據在網頁上」: 資料存取 >> 顯示
資料可以取用後可以下echo、print_r,可以顯示但是加工要「額外」處理,所以要再延伸會很牽強,所以display就不適合放在Data內
而函數職責屬於功能的實踐,除了可視範圍跟否定再延伸,還要考慮的是「功能複用度」
就有疑問的點來討論
「因為要"新增"數據到資料庫內之前,每次都一定要先確認數據沒問題」
我們將新增的這個功能替換成其他的詞句
「因為要"編輯"數據到資料庫內之前,每次都一定要先確認數據沒問題」
「因為要"刪除"數據到資料庫內之前,每次都一定要先確認數據沒問題」
如果要將
filter()包在指定方法內,那麼你相同的動作就都要重複包一次,這就不符合單一職責原則
那如果你的filter()有參數上的異動,你就要在包裝的功能內去修改filter(),這樣就不符合「開閉原則」
大致上會像是這樣
<?php
class Data {
/**
* 新增
* @return [type] [description]
*/
public function create() { /****/ }
/**
* 修改
* @return [type] [description]
*/
public function update() { /****/ }
/**
* 刪除
* @return [type] [description]
*/
public function delete() { /****/ }
/**
* 查詢
* @return [type] [description]
*/
public function query() { /****/ }
/**
* 過濾
* @return [type] [description]
*/
public function filter() { /****/ }
}
<?php
class Response {
private $headers = array();
private $level = 0;
private $output;
public function addHeader($header) {
$this->headers[] = $header;
}
public function setOutput($output) {
$this->output = $output;
}
public function getOutput() {
return $this->output;
}
public function display($data='') {
if ($this->output) {
if (!headers_sent()) {
foreach ($this->headers as $header) {
header($header, true);
}
}
echo $output;
}
}
}