今日は4日目に移りましょう。旧ルールでは、昨日の成果を振り返ることから始めます:
- Consolidate Conditional Expression
- Consolidate Duplicate Conditional Fragments
- Convert Procedural Design to Objects
リファクタリングとは?
単純に理解すると、理解しやすくし、修正コストを削減するために、観察可能な動作を変えずにソフトウェアの内部構造を改善することです。
これは、達成すべき目標とタスクのリストです。
- Decompose Conditional
- Duplicate Observed Data
- Encapsulate Collection
- Encapsulate Downcast
- Encapsulate Field
- Extract Class
- Extract Hierarchy
- Extract Interface
- Extract Method
- Extract Subclass
- Extract Superclass
- Form Template Method
- Hide Delegate
- Hide Method
- Inline Class
- Inline Method
- Inline Temp
- Introduce Assertion
- Introduce Explaining Variable
- Introduce Foreign Method
- Introduce Local Extension
- Introduce Null Object
- Introduce Parameter Object
- Move Field
- Move Method
- Parameterize Method
- Preserve Whole Object
- Pull Up Constructor Body
- Pull Up Field
- Pull Up Method
- Push Down Field
- Push Down Method
- Remove Assignments to Parameters
- Remove Control Flag
- Remove Middle Man
- Remove Parameter
- Remove Setting Method
- Rename Method
- Replace Array with Object
- Replace Conditional with Polymorphism
- Replace Constructor with Factory Method
- Replace Data Value with Object
- 委任を継承に置き換える
- Replace Error Code with Exception
- Replace Exception with Test
- Replace Inheritance with Delegation
- Replace Magic Number with Symbolic Constant
- Replace Method with Method Object
- Replace Nested Conditional with Guard Clauses
- Replace Parameter with Explicit Methods
- Replace Parameter with Methods
- Replace Record with Data Class
- Replace Subclass with Fields
- Replace Temp with Query
- Replace Type Code with Class
- Replace Type Code with State/Strategy
- Replace Type Code with Subclasses
- Self Encapsulate Field
- Separate Domain from Presentation
- Separate Query from Modifier
- Split Temporary Variable
- Substitute Algorithm
- 遺産相続
Decompose Conditional
説明: 複雑なif ...else if ... else文があります。.elseステートメントがあります。
動機:ビジネス開発では、異なる条件分岐をチェックし、異なる分岐に基づいて異なることを行うコードを書かなければなりません。そして、かなり長い関数や、それ自体がコードを読みにくくする大きな関数や、コードを読みにくくする条件ロジックが出てきます。
//冬と夏で単価の違う商品を買ったときの合計金額を計算したいとする:
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * _winterRate + _winterServiceCharge;
} else {
charge = quantity * _summerRate;
}
各ブランチの判定条件を以下のように別の関数に抽出しました:
if(Summer(date)){
charge = winterCharge(quantity);
}else{
charge = summerCharge(quantity);
}
// ...次のコードは
private Summer(date:Date):boolean{
return date.before(SUMMER_START) || date.after(SUMMER_END);
}
private summerCharge(quantity:number):number{
return quantity*summerRate;
}
private winterCharge(int quantity){
return quantity* winterRate+_winterServiceCharge;
上のコードを見ればわかるように、全体の構造がより明確になっています。また、辞書を使うという神業もあります。
const result = 'a';
// bad
if(result == "a"){
...
}else if(result == "b"){
...
}else{
...
}
//best 辞書を採用する
interface IDictionaries {
[key: string]: any;
}
const obj: IDictionaries = {
a: () => {},
b: () => {},
};
const fn = obj[result];
fn && fn();
Duplicate Observed Data
説明 : ドメイン関数がアクセスする必要のあるデータがGUIコントロールにあります。そのデータをドメインオブジェクトにコピーし、Observerパターンを作成して、ドメインオブジェクトとGUIオブジェクト内の重複データを同期します。
上記のドメインオブジェクトは、多くのWebフロントエンドの学生にとって非常に漠然としたものであり、一緒に学ぶ必要があります。
"ドメインオブジェクトは、エンティティクラスとも呼ばれ、ビジネスの状態を表し、プレゼンテーション層、ビジネス層、永続化層を介して、最終的にデータベースに永続化され、言葉の簡単な理解だけであれば、ドメインオブジェクトは、Javaクラスに対応するデータベースのテーブルとみなすことができます。フロントエンド開発に対応する場合も、データラでVueです。
ドメインオブジェクトといえば、ドメイン駆動型モデルを語る必要があります。ビジネスロジックはサービスに記述され、WmActPoiはせいぜいデータキャリアであり、何の振る舞いもない、貧弱なモデルですが、次の図は、DDDプロセスの全体的な動作です。
DDDのデザイン思考では、ビジネスロジックはもはや少数の大きなクラスに集中するのではなく、独自のステートとビヘイビアを持つ、比較的小さな多数のドメインオブジェクトで構成され、各クラスは比較的完全で独立した本体であり、実際のドメイン内のビジネスオブジェクトにマッピングされます。ドメインモデルは、このような多数の細かいクラスで構成されています。以下に、実際のDDDのための非常にコアなTypeScriptコードを掲載します。
// user.ts import { AggregateRootProperties, AggregateRoot, Uuid } from '@node-ts/ddd' import { UserRegistered, UserPasswordChanged, UserDisabled } from './events' import { OAuthService } from './services' export interface UserProperties extends AggregateRootProperties { email: string isEnabled: boolean passwordChangedAt: Date | undefined } export class User extends AggregateRoot implements UserProperties { email: string isEnabled: boolean passwordChangedAt: Date | undefined // Creation static method. Aggregates are never "newed" up by consumers. static register (id: Uuid, email: string): User { const userRegistered = new UserRegistered( id, email, true ) const user = new User(id) // event is applied to the user object user.when(userRegistered) return user } /** * Changes the user's password that's used to log in to the site * @param oauthService the oauth service that hosts the user account * @param newPassword password the user wants to use */ async changePassword (oauthService: OAuthService, newPassword: string): Promise<void> { // A domain service is used to perform the actual change of password await oauthService.changePassword(this.id, newPassword) const userPasswordChanged = new UserPasswordChanged( this.id, new Date() ) super.when(userPasswordChanged) } /** * Disable the user account so they can no longer log in */ disable (): void { const userDisabled = new UserDisabled(this.id, false) super.when(userDisabled) } protected whenUserRegistered (event: UserRegistered): void { this.email = event.email this.isEnabled = event.isEnabled } protected whenPasswordChanged (event: UserPasswordChanged): void { this.passwordChangedAt = event.passwordChangedAt } protected whenUserDisabled (event: UserDisabled): void { this.isEnabled = event.isEnabled } }
動機 : うまく階層化されたシステムは、ユーザーインターフェースを扱うコードとビジネスロジックを扱うコードを分離する必要があります。ビジネスロジックがユーザーインターフェイスに組み込まれているような2階層で開発されたコードに遭遇した場合、重要なタスクは関数の分解と移動であり、振る舞いを分離する必要があります。データをただ移動させることはできません。データを新しいオブジェクトにコピーし、同じ同期メカニズムを提供しなければなりません。
// 実際、このルールは俗に言うフロントエンドであると言われている。
// 現在、フロントエンドの開発者はVueやMobxなどを使ってこれを処理している。しかし、これだけに対処するために、Rxがある。.js
const locations = new RX.Observable((observer) => {
let watchId: number;
if ('geolocation' in navigator) {
watchId = navigator.geolocation.watchPosition((position: Position) => {
observer.next(position);
}, (error: PositionError) => {
observer.error(error);
});
} else {
observer.error('Geolocation not available');
}
return {
unsubscribe() {
navigator.geolocation.clearWatch(watchId);
}
};
});
const locationsSubscription = locations.subscribe({
next(position:Position) {
console.log('Current Position: ', position);
},
error(msg:string) {
console.log('Error Getting Location: ', msg);
}
});
setTimeout(() => {
locationsSubscription.unsubscribe();
}, 10000);
このような上記のデモは、位置情報を聞いているように、もちろん、この場所はまた、バックエンドの要求データであることができ、それを単刀直入に言えば、一緒に場所を聞くために、マルチロケーションではなく、独自のクラスを維持するためにそれぞれのクラスに分散しています。
Encapsulate Collection
説明 : 関数にコレクションの読み取り専用コピーを返させ、コレクション要素の追加と削除を行う関数をこのクラスで提供します。
動機 : コレクションがクラスで使用されることはよくあり、通常、そのようなクラスはコレクションのフェッチ関数とセット関数も提供します。このとき、fetch関数はコレクション自体を返すべきではありません。これは、ユーザーがコレクションの所有者に何も知られることなくコレクションの内容を変更できるようにするためで、オブジェクトの内部データ構造に関する多くの情報を公開することにもなります。さらに、セット関数は追加、変更、削除の操作を提供するのではなく、コレクションを直接オーバーライドすべきではありません。これが実行されると、コレクションはうまくカプセル化され、コレクションとユーザーの間の結合が減少します。
このルールはそれほど複雑なものではないので、具体的なコードは省略します。
1日目のまとめ :
2日目のまとめ :
3日目のレビュー記事: