Object-Oriented Analysis and Design
by Hohshen
identifiability可辨識性
Attributes屬性
Behavior行為(Operation動作操作)
透過一些行為, 與其他物件發生關係(Relationship)
描述著各個物件中的互動
There are too many objects (instances) and we need to classify them!
一群...
1. Relationship 和 object 相同
2. 都有著相同的Responsibility, 並朝著相同目標前進
如果有些不同=>使用abstract class
1. Explain the responsibilities of each class
2. class relationship
1. Structure
Class Diagram
Object Diagram
2. Behavior
Phase I: Require Analysis, Create Domain Model
1. Dependency 依賴
2. Association 關聯
3. Generalization繼承(一般化)
如果給你一份需求,
你就能立刻看出需求中涉及的物件/類別以及之間的關係的話,
那麼這就是「架構式思維」的培養基礎
e.g. 我使用鉛筆寫字
I use pencil to write
Human
-------
-------
+ write(pencil:Pencil)Pencil
-------
-------e.g. 老師教這個班級
Teacher teach this class
Teacher
-------
-------
+ teach(class:Class):voidClass
-------
-------? teach ?
e.g. 老師教這個班級
Teacher teach this class
Teacher
-------
-------
+ teach(class:Class):voidClass
-------
-------1 teach *
加上數量
how many object of teach relationshiop. focus on teach operation
How many classes can a teacher teach? n-th class
How many teachers can there be in a class? 1-th teacher
is an one-to-many association
e.g. 學生修課, 並取得期末成績
Every student who takes this class will get a final score
Student
-------
-------
+ takeClass(class:Class):voidClass
-------
-------* take *
Every student who takes this class
e.g. 學生修課, 並取得期末成績
Every student who takes this class will get a final score
Student
-------
-------
+ takeClass(class:Class):voidClass
-------
-------* take *
(relationship) will get a final
TakeClass
-------
- finalScore: int
-------1 get 1
e.g. A man is a human, and a woman is also a human
Woman
-------
-------
Man
-------
-------Human
-------
-------e.g. Both men and women will think.
Woman
-------
-------
think():voidMan
-------
-------
think():void/Human
-------
-------
+ /think():void
Abstract class (italics)
Abstract Method (italics)
A 知道 B A--->B
AB相互 A<--->B or A---B
知道、使用的意思
0...* love 1
請表示暗戀關係
(限定人與人, 暫不考慮人獸)
(many)Boy love a Girl, But girl not know.
Boy
--------
--------
+ love():voidGirl
--------
--------
+ love():void0...* crush1
(many)Boy love a Girl, But girl not know.
Boy
--------
--------
+ love():voidGirl
--------
--------
+ love():void0...* crush 1
Boy
--------
--------
+ love():voidGirl
--------
--------
+ love():void0...* crush 1
Boy
--------
--------
+ love():voidGirl
--------
--------
+ love():void0...* crush 1
Person
--------
--------
+ love():void- 暗戀者
- 被暗戀者
Person
--------
--------
+ buy(house:House):voidHouse
--------
--------
1 Buy 0...*
擁有者獲得方塊
e.g. One person can buy many houses
自住客
屯房客
Directory
--------
--------
+ contain(file:File):voidFile
--------
--------
1 contain 0...*
Q1: Can't files be independent of directories?
Q2: When deleting a folder, will the files also be deleted?
Yes => use combination
擁有者獲得方塊
e.g. The directory on my computer contains many files
Dependency
Generalization
Association
Aggregation
Composition
/RelationshipAssociationAggregationCompositionGeneralizationDependency重點在於「知識」, 不是程式實作
這樣在程式中 work 嗎?
這樣的設計好嗎?
會不會有太多的條件式?
這邊是不是要套用什麼模式?
這邊是不是太多依賴?太過耦合?....
領域知識包含:定義、規範、邏輯、行為/流程
1. 捕捉知識點: 捕捉各個腳色
2. 建立關係
Let’s start OOA to OOP
class Student{
private id: number
private age:number
constructor(id:number,age:number){
this.id=id;
this.setAge(age);
}
public setAge(age:number):void{
if(age<7||age>150){
throw new IllegaArgumentExecption("Age muse be with 7~150");
}
this.age=age;
}
public study():void{
console.log("Study")
}
}class IllegalArgumentExecption extends Error{
consturctor(message:string){
super(message)
}
}getter/setter不會特別寫在Class Diagram
使用setter做Initialization verification
Student
--------
- id: int
- age: int {7<=age<=150}
--------
+study():void
class Student{
private id: number
private age:number
constructor(id:number,age:number){
//...
}
private setAge(age:number):void{
//...
}
public study(book:Book):void{// here
console.log("Study" + book.getName());
}
}Student
--------
- id: int
- age: int {7<=age<=150}
--------
+study(book:Book):voidclass Book{
private name:string
constructor(name:string){
this.name=name;
}
public getName(){
return this.name;
}
}Book
-------
- name:string
-------class Student{
private id: number
private age:number
private subjects: Subject[]=[]
constructor(id:number,age:number){
//...
}
private setAge(age:number):void{
//...
}
public study(subject:Subject):void{// here
console.log("Study" + subject.getName());
this.subjects.push(subject)
}
}Student
--------
- id: int
- age: int {7<=age<=150}
--------
+study(subject:Subject):voidclass Subject{
private name:string
constructor(name:string){
this.name=name;
}
public getName(){
return this.name;
}
}Subject
-------
- name:string
-------1 study 0...*
使用subject array代表一直有著多個Subject
class Student{
private subjects: Subject[]=[]
//...
public study(subject:Subject):void{
console.log("Study" + subject.getName());
this.subjects.push(subject)
subject.setLearner(this)// here
}
}Student
--------
- id: int
- age: int {7<=age<=150}
--------
+study(subject:Subject):voidclass Subject{
private learner:Studnet
//...
public setLearner(lerner:Studnet){
this.learner=learner
}
}Subject
-------
- name:string
-------1 study 0...*
Subject提供setLearner
Student將自己設為Subject的Learner
雖然會照成循環依賴,
但許多場合中能夠”有效地化簡程式複雜度”
比較怕的是consturctor的circular dependency
class Student{
private scores: Score[]=[]
//...
public setScore(score:Score):void{
this.scores.push(score)
}
}這次並非相互持有對方,
而是雙方持有同一份註冊紀錄
Student
-------
-------
+ takeClass(class:Class):voidClass
-------
-------* take *
TakeClass
-------
- finalScore: int
-------1 get 1
class FinalScore{
private score:number
constructor(
private class:Class
private student:Studnet,
private score:number,
){
this.class=class;
this.student=student;
this.score=score;
}
}class Class{
private scores: Score[]=[]
public exam(student:Student,score:number){
const score=new Score(this,student,score);
student.setScore(score) //here
this.setScore(score) //here
}
public setScore(score:Score){
this.scores.push(score)
}
}同理 Association
就是繼承
等等介紹的字
都來自於Design Pattern
Pattern的概念
新手:
軟體設計的快速指引
老手:
精通模式,內化了設計的本質,
將軟體從束縛中解放
Alexander從數學的觀點把大量建築案例分析,
抽象化成當使用者與建築空間互動中,
提供了253不同解決問題的模式(Pattern)
造就了四人幫GoF"設計模式"聖經
品質是"誕生"出來的,
而非被"人造"出來的
– Alexander
的永恆建築之道
e.g. 花
無論是建築物還是軟體程式,
基於pattern language 的文法,
在限定的context下,
套用patterns 來解決force和problem
孕育出生命力
從而獲得the Quality without a name
讓“特定品質”自然誕生;
這個種子就是模式語言Pattern Language
使用相同的文法,種出具有相同品質的結果
充滿中國風的建築
如何凸顯中國風? 建築物的元素體現了他的特色
元素: 就是它蘊含的所有模式(e.g. 燕尾脊)
以及模式與模式之間的關係
把這些關係當作一種文法,
就是所謂的Pattern Language(模式語言)
其實你無法消除這些force的存在,
只能透過建築物的型態(form)的設計
使得這些forces讓他們朝著特定方向釋放而去
如果沒有妥善處理,就會照成彼此衝突,
失去生命力
從一次次的force中,
順應著force,發展出各式各樣的模式,
打造出各式建築
1. 在Context下找到Problem.
2. 這個Problem下遇到了什麼Force,
以造成此Problem.
3. 將每個Force提出解決方案Solution
4. 最後得到了Resulting Context
a pattern is a solution to a problem in a context
每個模式都次在每個情境下,針對一個問題提出的解決方案
context: 你目前關心的項目(情境)
e.g. context: 肥宅工程師 problem: 如何變瘦? force: 1. 重訓好無聊 2. 效果慢,容易放棄 solution: 改上拳擊課 resulting context: 不但變瘦,還學習到了拳擊技巧
=>肥宅變瘦模式
不過我們無法透過文字來描述這些solution,
所以必須繪製出這些solution的形狀 — form 以表達這個模式
所以我們會使用form來代稱solution,
最常來表達form的就是使用uml
One pattern at a time
遇到Problem=> 尋找pattern,找到對應的pattern
基於pattern的語法, 藉由[重構三步驟]來重構
1. Encapsulation what varies 封裝變動處
2. Abstract common behaviors 萃取共同行為
3. Delegation/Composition 委派或複合
這三個步驟,幫你形塑出了該pattern的form,
化解了所有forces
Person
-------
-------OOA
/Car
-------
+ 載(person:Person):void
+ /發動引擎():void
-------/Motorcycle
-------
+ 載(person:Person):void
+ /發動引擎():void
-------電動車
-------
+ 發動引擎():void
-------油車
-------
+ 發動引擎():void
-------普通機車
-------
+ 發動引擎():void
-------Person
-------
-------OOD
電動車
-------
-------
+ 發動引擎():void油車
-------
-------
+ 發動引擎():void
普通機車
-------
-------
+ 發動引擎():void
<<interface>> 交通工具
-----
-----
+ 發動引擎():void
+ 載(person:Person):void
/Motorcycle
-------
-------
+ 載(person:Person):void
+ /發動引擎():void/Car
-------
-------
+ 載(person:Person):void
+ /發動引擎():voidpublic void attack(hero:Hero){
switch(attackType){
case "NormalAttack":
hero.damage(10)
case "MagicAttack":
hero.damage(20)
case "UltimateAttack":
hero.damage(30)
}
}Force:
1. 行為變動性
2. 維護性
3. 擴充性
public void attack(hero:Hero){
switch(attackType){
case "NormalAttack":
hero.damage(10)
case "MagicAttack":
hero.damage(20)
case "UltimateAttack":
hero.damage(30)
}
}class NormalAttack
+ attack(attacker:Hero, attacked:Hero):void
}
class MagicAttack
+ attack(attacker:Hero, attacked:Hero):void
}
class UltimateAttack
+ attack(attacker:Hero, attacked:Hero):void
}決定屬性(Attribute)和行為(Behavior)的可視範圍(Visibility)
=>透過封裝隱藏了一些屬性和行為
針對每個行為變種,創建一個類別來去封裝他
class UltimateAttack{
+ attack(
attacker:Hero,
attacked:Hero
):void
}查看這些class的共同處,生出interface
透過介面,是無法看出完整atack行爲的
class NormalAttack{
+ attack(
attacker:Hero,
attacked:Hero
):void
}class MagicAttack{
+ attack(
attacker:Hero,
attacked:Hero
):void
}interface AttackType{
+ attack(
attacker:Hero,
attacked:Hero
):void
}class UltimateAttack{
+ attack(
attacker:Hero,
attacked:Hero
):void
}委派
hero委派atack職責,給attackType介面
class NormalAttack{
+ attack(
attacker:Hero,
attacked:Hero
):void
}class MagicAttack{
+ attack(
attacker:Hero,
attacked:Hero
):void
}interface AttackType{
+ attack(
attacker:Hero,
attacked:Hero
):void
}Hero
-------
hp:int
attackType:AttackType
-------
attack(hero:Hero):voidUltimateAttackNormalAttackMagicAttackAttackTypeHeroUltimateAttackNormalAttackMagicAttackHero因為依賴的方向確實被反轉了
原本hero 依賴attack行為區塊
最厲害的是透過DI的方式
在不修改Context內部程式碼的前提之下
不斷地擴充新的handler來支援新的需求(OCP)
Context
Handler
Handler
Handler
Handler
學會察覺到Forces
真正對軟體設計入木三分的人所設計出的軟體
沒有一行程式碼是多餘的, 要怎麼做到這件事呢?
需要大量的練習,藉由一次次的經驗
來鍛鍊察覺forces的能力,
並且清晰地說出你究竟是套用了何種pattern,來解決forces
避免Over Design
| 工程師技能 | 戰鬥技能 |
|---|---|
| 熟習程式語言 | 強壯身體 |
| 熟悉後端知識 | 懂武術 |
| solid + ooad | 懂排陣法 |
| Clean Arch | 懂兵法 |
目標: 解耦合高內聚
目標: 贏的漂亮
got a require document
OOA
OOP version 1
feel something wrong! find out force and problem
OOD use design pattern solve force
OOP version 2
Every design needs to be drawn first! remember forever!
Architecture thinking: text to tech
The architectural thinking of software engineering is based on the relationship of text to objects and classes.
TODO
TODO
TODO
TODO