Re: [概念] single responsibility principle
※ 引述《hsnucsc (hsnugo)》之銘言:
: 我找了很多網站
: 都將解釋SRP成: 每一個物件, 應該要只有單一的responsibility
: 而將responsibility解釋成: 只有一個理由去改變物件
: 但是我還是覺得responsibility這個詞很模糊
: (如果直接翻譯成中文, "責任", 仍然很難知道這個責任的大小)
這個責任是一個夠大的責任。
因為你不是從標準路線走到這一步,
所以大概跟你說明一下好了。
傳統 use-case driven 的開發,
會先從零散的問題裡整理出比較明確的 problem statements,
接著從那裡面區分出 functional/non-functional requirements 來。
use-case model 主要在模塑的是 functional requirements,
經過對前面得到的文字資料做名詞和動詞的分析後,
可以找到 actors 和 use cases。
在 use cases 被找到之後,
你的「需求分析工具」會要求你撰寫 use case specifications,
格式大概是:
Use Case Specification: <這裡填 use case 的名字>
1. Use Case Name
1.1 Brief Description
2. Flow of Events
2.1 Basic Flow
2.2 Alternative Flows
3. Special Requirements
...
在 2. 的部分要求你描述的,
就是整個 use case 從開始到結束要做的事情。
所謂的 basic flow 是很順利的一直線走到底的流程,
沒有所謂的特例狀況和分支,
特例狀況和分支都被描述在 alternative flows 上。
一般來說使用 UML 開發的話,
這部份也時常搭配 activity diagrams 來進行。
前言就講到這裡,
有一點長;
主要目的只有告訴你什麼叫 flow of events,
還有它是怎麼來的。
flow of events 是所謂「需求規格」的一部份。
對 flow of events 做 textual analysis,
你將會得到一群名詞和動詞,
一個有效的名詞一般來說就會成為一個 class。
我想到這邊你還是很熟悉,
但是這背後還有其它的意義。
你在描述 flow of events 時,
也同時在描述每個名詞對某些行為的責任。
SRP 並不是無限上綱到脫離 requirements 讓你停不下來,
你的一切 analysis 和 design 都必須基於 requirements 進行。
就像做學術研究,
一個論文題目總是不會解決所有問題,
而是會設定一些限制條件後再對有限的問題進行拆解;
實務面的軟體設計更是如此。
責任的大小,
完全視你的需求而定,
不然你所有的東西恐怕都要定義到原子或夸克去了。
進行 SRP 分析或填寫 CRC 卡的時候,
請記得看著 flow of events 做,
如果你沒有這種東西,
就看看 flow charts 或 activity diagrams。
這些東西裡都沒有描述到的更細部責任,
那就不需要考慮,
你不需要把責任 delegate 給一個無效甚至不存在的名詞。
在設計階段,
SRP 常跟 OCP 搭配,
而 OCP 常跟 strategy pattern 搭配。
strategy pattern 負責將各種行為做分類,
因此改變行為常常是「新增」 concrete strategy,
以及對 factory method (或 creation method) 的修改。
除非你需要新增的是 strategy 的 family (即 abstract strategy 或 interface),
才有可能需要去動到 context class。
因為這三種東西通常都有連動關係,
所以當你的「行為」本身根據需求並不會有「一個家族」的行為集時,
你是不需要再另外把責任再往外 delegate 給其它 classes。
以上兩段應該大概可以告訴你「什麼時候該停下來」了,
簡單來說,
一切都環繞在需求規格上;
其實我也很想直接推一句「看需求決定」來回你就好了,
但是你可能會覺得很抽象,
然後將來可能又會有其他人跑來問一樣的問題。
: 以書上的例子而言
: Automobile 車子有很多功能
: start()
: stop()
: changeTiles(Tile [*])
: drive()
: wash()
: checkOil()
: getOil()
: 應該要被分解成
: Automobile
: start()
: stop()
: getOil()
: Driver
: drive(Automobile)
: Carwash
: wash()
: Mechanic
: changeTires(Automobile, Tire[*])
: checkOil(Automible)
: 有一些method移交到其他物件處理
: 但是....
: 1. 假設Automible有
: body, front-left_tire, front_right_tire, back-left-tire, back_right_tire
: changeTires()為了要change車子的輪胎
: 勢必要改變其四個輪胎
: 我猜寫法是
: changeTires(Automobile au, Tire[*] tires)
: au.set_front_left_tire(tires[0]);
: au.set_front_right_tire(tires[1]);
: ....
: 這樣為什麼不直接把changeTires這件事直接委派給Automible去做
: 同樣的道理, 如果其他drive, wash等method, 需要get or set
: 來知道Automible的細節, 那何不直接把這樣工作交給Automible做
你把這世界簡單化了。
一方面也是因為你看的書不是從頭到尾 run 一個 project 給你看,
確實很容易讓你因為對「需求」的資訊不足,
使得你對例子的理解與作者假設的情境不同。
一個物件被委派責任的原因,
主要是因為它「瞭解如何完成這個任務」,
也就是說包括這項任務的細節在內,
並不是你想的單純 set/get 就好了,
Automible 本身可能提供非常多 method 讓你 set/get 細部的參數
但是只有 Mechanic 知道要如何正確的 set 它們,
這中間可能還會有一些操作機械的流程,
可能還會有一些基本的安全檢測等等;
當然這一些要看你的 flow of events 有沒有描述到。
: 2. 這樣好像又回到procedural programming
: data跟function是分開的, data當作function的input後, 由function來處理data
: 在這裡, Automobile是要處理的data
: 而Driver, Carwash, Mechanic則有點像是處理data的function
: 再舉個例子:
: 今天我們需要一個鐵路線路網, 上面有多條線路, 多個車站
: 且在給予起點和終點後, 可以找出一個起點到終點的path, 並印出來
: 如果是我來設計的話
: class會有Subway, Line, Station, Route
: Subway.find(Sation s, Sation t)後, return 一個Route
: Route.print()印出path
: 但是書上的方法,
: class會有Subway, Connection, Station, Printer
: (Line, Connection的差異先不討論)
: 他用Printer來印一個List route(有Connection跟Station)
: 於是, Printer在print的method裡
: 必須要不停的用route.getXX(), station.getXX() ......
: 這感覺很像是Printer在處理一個Data
: 為什麼不直接把print的工作給statoin, 給route呢
Printer 可以做相當簡化的工作,
比方說它就是只提供 print(String),
也就是字串的列印;
station 和 route 並不需要知道如何列印一個字串。
Route.print() 可能要做比你想像中還要多的事情,
比方說把內部的資料表示法轉換成有意義、易讀易懂且可列印的字串,
才把列印「字串」的責任交給 Printer。
: java Exception 不是也有 printStackTrace, 並沒有說特別把print的工作交給別人啊?
: 上面是增加新的物件來處理
: 我也有找到另一個方法, 是增加interface
: http://www.codingefficiency.com/2009/07/18/
: solid-s-single-responsibility-principle/
: http://0rz.tw/QP6hs (縮過的網址)
: 不過仍然讓人很難分辨responsibility
: 說嚴格一點, 好像每個method, 都有理由改變
: 但是又不可能把每個method都新增一個物件去處理這項功能
: 希望有人可以幫忙解答
: 謝謝
--
Ling-hua Tseng (uranus@tinlans.org)
Department of Computer Science, National Tsing-Hua University
Interesting: C++, Compiler, PL/PD, OS, VM, Large-scale software design
Researching: Software pipelining for VLIW architectures
Homepage: http://www.tinlans.org
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 118.160.104.199
※ 編輯: tinlans 來自: 118.160.104.199 (01/21 15:40)
推
01/21 18:53, , 1F
01/21 18:53, 1F
推
01/23 00:28, , 2F
01/23 00:28, 2F
討論串 (同標題文章)
本文引述了以下文章的的內容:
完整討論串 (本文為第 2 之 5 篇):
OOAD 近期熱門文章
PTT數位生活區 即時熱門文章