「既存のコード2の設計のリファクタリングと改善」機能とデータの再編成

/**
 @startTime 2020-12-20 15:30
 @endTime 2020-12-20 21:00
 @startPage 103 
 @endPage 130
 @efficiency 130/5 = 26页/天
 @needDays 412/26 = 16天
 @overDay 2020-12-16 + 16天 = 2020-12-31
*/

第5章リファクタリングリスト

第5章から第12章は、リファクタリングリストのドラフトを構成します。リストされているリファクタリング手法は、作成者の長年のプログラミング経験に基づいています。

1.再構築されたレコード形式

  1. 最初は名前です。リファクタリングボキャブラリーを構築するには、名前が非常に重要です。
  2. 名前の後に簡単な要約(要約)があります。このリファクタリング手法の適用可能なシナリオとその機能を簡単に紹介します。この部分は、必要なリファクタリング手法をすばやく見つけるのに役立ちます。
  3. モチベーションは、「なぜこのリファクタリングが必要なのか」と「どのような状況でこのリファクタリングを使用すべきでないのか」を紹介します。
  4. メソッド(メカニズム)は、このリファクタリングを実行する方法を段階的に説明します。
  5. 例は、このリファクタリング手法がどのように機能するかを示す非常に簡単な例を示しています。

2.参照点を見つける

やみくもに検索して置き換えないでください。すべての参照ポイントをチェックして、置き換えたいものを指していることを確認する必要があります。
コンパイラに参照ポイントのキャプチャを支援させることができますが、これには次のようないくつかの問題もあります。

  1. 削除された部分が継承システムで複数回宣言されている場合、コンパイラは混乱します。
  2. コンパイラが遅すぎる可能性があります。
  3. コンパイラは、リフレクションメカニズムによって取得された参照ポイントを見つけることができません

3.これらの再構築技術はどの程度成熟していますか

小さなステップで進み、頻繁にテストします。
デザインパターンは、リファクタリングの目標を提供します。
パターンと再構築の間には本質的な関係があります。パターンは達成したい目標であり、再構築は到達する方法です。

第6章機能の再編成

私のリファクタリングアプローチの大部分は、関数を整理して、コードをより適切にパッケージ化することです。

1.リファイン機能

コードの一部を独立した関数に入れ、関数名に関数の目的を説明させます。
"動機"

  1. 各関数の粒度が非常に小さい場合、関数が再利用される可能性は高くなります。
  2. 高レベルの関数を一連のコメントのように読み取らせます。
  3. 関数がきめ細かい場合、関数を上書きするのは簡単です;
    適切な関数はどのくらいの期間ですか?私の意見では、長さは問題ではありません。重要なのは、関数名と関数本体の間のセマンティックな長い文にあります。改良によってコードの明確さが向上する場合は、それを実行します。関数名が改良されたコードより長くても、問題ではありません。

2.インライン機能

関数呼び出しポイントに関数本体を挿入してから、関数を削除します。

3.インライン一時変数

変数へのすべての参照を、それに割り当てられている式自体に置き換えます。

4.一時変数をクエリに置き換えます

この式を独立した関数に抽出し、この一時変数のすべての参照ポイントを新しい関数の呼び出しに置き換えます。その後、新しい機能を他の機能で使用できるようになります。

5.説明変数を導入する

複雑な式の結果を一時変数に入れ、変数名を使用して式の目的を説明します。
「動機付け」
条件付きロジックでは、説明変数の導入が特に重要です。この再構成を使用して各条件付き句を抽出し、適切な名前の一時変数を使用して、対応する条件付き句の意味を説明できます。この再構成のもう1つの用途は、より長いアルゴリズムでは、一時変数を使用して各ステップの意味を説明できることです。

6.一時変数を分解します

割り当てごとに、独立した対応する一時変数が作成されます。
「動機付け」
一時変数が複数回割り当てられている場合は、それらが関数内で複数の責任を負っていることを意味します。一時変数が複数の責任を負う場合は、それぞれが1つの責任のみを負う複数の一時変数に置き換える必要があります。同じ一時変数が2つの異なることを実行するため、コードリーダーが混乱します。

/**
 @startTime 2020-12-21 21:30
 @endTime 2020-12-21 23:50
 @startPage 131 
 @endPage 188
 @efficiency 188/6 = 31.3页/天
 @needDays 412/31.3 = 13天
 @overDay 2020-12-16 + 13天 =  2020-12-28
*/

7.パラメータの割り当てを削除します

パラメータの位置を一時変数に置き換えます。
まず、「パラメータに値を割り当てる」の意味を全員に明確に伝えたいと思います。userという名前のオブジェクトをパラメーターとして関数に渡す場合、「パラメーターの割り当て」とは、別のオブジェクトを参照するようにユーザーを変更することを意味します。あなたが「入ってくるオブジェクト」で何かをするなら、それは大丈夫です、私はいつもそれをします。「ユーザーが別のオブジェクトを指すように変更された」という状況についてのみ説明します。

8.関数を関数オブジェクトに置き換えます

ローカル変数を使用すると抽出関数を使用できない大きな関数があります。この関数を別のオブジェクトに配置すると、ローカル変数がオブジェクトのフィールドになり、同じオブジェクトで使用できるようになります。この大きな関数を複数の小さな関数に分割します。
「モチベーション」
この本では、読者に小さな機能の美しさを強調し続けています。大きな関数から比較的独立したコードを抽出する限り、コードの読みやすさを大幅に向上させることができます。
"練習"

  1. 新しいクラスを作成し、処理する関数の目的に応じてこのクラスに名前を付けます。
  2. 新しいクラスに最後のフィールドを作成して、元の大きな関数が配置されているオブジェクトを保存します。このフィールドを「ソースオブジェクト」と呼びます。同時に、元の関数の各一時変数と各パラメーターについて、新しいクラスに対応するフィールドを作成して保存します。
  3. 新しいクラスにコンストラクターを作成し、ソースオブジェクトのすべてのパラメーターと元の関数をパラメーターとして受け取ります。
  4. 新しいクラスに計算関数を作成します。
  5. 元の関数のコードをcompute()関数にコピーします。ソースオブジェクトの関数を呼び出す必要がある場合は、ソースオブジェクトフィールドから呼び出してください。
  6. コンパイル。
  7. 古い関数の関数本体をcompute()関数に置き換えます。

9.置換アルゴリズム

関数本体を別のアルゴリズムに置き換えます。

第7章オブジェクト間で機能を移動する

1.移動機能

プログラムには、クラス外の別のクラスとより多くの通信を行う関数があります。関数が最も頻繁に参照するクラス内で同様の動作をする新しい関数を作成します。古い関数を純粋な委任関係に変えるか、古い関数を完全に削除します。
「伝達機能」は再構成理論の根幹です。クラスの動作が多すぎる場合、またはクラスが他のクラスとの協力が多すぎて高度な結合を形成できない場合は、関数を移動します。これにより、システム内のクラスをより単純にすることができ、これらのクラスは最終的に、システムによって提供されるタスクをよりクリーンに実行します。

2.フィールドを移動します

3.精製

特定のクラスは、2つのクラスで実行する必要があることを実行し、新しいクラスを作成し、関連するフィールドと関数を古いクラスから新しいクラスに移動します。

4.クラスをインライン化します

特定のクラスはあまり機能せず、このクラスのすべての特性を別のクラスに移動してから、クラスを削除します。

5.委員会の関係を隠す

クライアントがデリゲートクラスを介して別のオブジェクトを呼び出すと、クライアントが必要とするすべての機能がサービスクラスで確立され、デリゲート関係が非表示になります。

public Person getManager(){
    
    
	return _department.getManager();
}

部門のすべての機能の委任関係を完了し、それに応じてPersonのすべての顧客を変更した後でのみ、Personのアクセス関数getDepartment()を削除できます。

6.仲介者を削除します

特定のクラスが単純な委任アクションを実行しすぎる場合は、クライアントに委任されたクラスを直接呼び出させます。

7、追加機能の導入

Date start = new Date(previous.getYear(),previous.getMonth(),previous.getDate()+1);

になる

Date start = nextDay(previous);
private static Date nextDay(previous.getYear(),previous.getMonth(),previous.getDate()+1);

少し理解しているようです。
結局のところ、関数の追加は一時的な手段です。可能であれば、これらの関数は本来あるべき場所に移動する必要があります。

8.ローカル拡張機能を導入します

サービスクラスにいくつかの追加関数を提供する必要がありますが、このクラスを変更したり、新しいクラスを作成したり、これらの追加関数を含めたり、この拡張クラスを元のクラスのサブクラスまたはラッパークラスにすることはできません。
「動機付け」
サブカテゴリーとパッケージングカテゴリーのどちらかを選択するとき、私は通常、必要な作業が少ないサブカテゴリーを好みます。サブカテゴリを作成する上での最大の障害は、元のデータが変更可能である場合、1つの変更アクションで2つのコピーを変更できないことです。この場合、パッケージ化カテゴリ「練習」に切り替える必要があります。

  1. 元のクラスのサブクラスまたはラッパークラスとして拡張クラスを作成します。
  2. 拡張クラスに変換コンストラクターを追加します。いわゆる変換コンストラクターは、「元のオブジェクトをパラメーターとして受け入れる」コンストラクターを指します。サブクラス化スキームが採用されている場合、変換コンストラクターは適切なスーパークラスコンストラクターを呼び出す必要があります。パッケージングスキームが採用されている場合、変換コンストラクターは、デリゲートを受け入れるためのインスタンス変数の形式で取得した着信パラメーターを保存する必要があります。元のオブジェクト。
  3. 拡張クラスに新しい機能を追加します。
  4. 必要に応じて、元のオブジェクトを拡張オブジェクトに変更します。
  5. 元のクラスに定義されているすべての追加関数を拡張クラスに移動します。

第8章データの再編成

1.自己カプセル化フィールド

フィールドの値/設定関数を確立し、これらの関数のみを使用してフィールドにアクセスします。

2.データ値をオブジェクトに置き換えます

データアイテムをオブジェクトにするために、他のデータや動作と一緒に使用する必要があるデータアイテムがあります。

3.値オブジェクトを参照オブジェクトに変更します

4.参照オブジェクトを値オブジェクトに変更します

小さく、不変で、管理が難しい参照オブジェクトがあります。この場合、それを値オブジェクトに変換します。
参照オブジェクトと値オブジェクトのどちらかを選択するのは簡単ではない場合があります。選択をした後、あなたはしばしば戻る方法を必要とします。
参照オブジェクトが使いにくくなった場合は、値オブジェクトに変換する必要があります。参照オブジェクトは何らかの方法で制御する必要があり、常に適切な参照オブジェクトをそのコントローラーに要求する必要があります。それらは、メモリ領域間の複雑な関連付けを引き起こす可能性があります。分散システムと並行システムでは、同期の問題を考慮する必要がないため、不変の値オブジェクトが特に役立ちます。

5.配列をオブジェクトに置き換えます

配列をオブジェクトに置き換えると、配列内の各要素がフィールドで表されます。
「モチベーション」
配列は、データを整理するために使用される一般的な構造です。ただし、これらは「類似したオブジェクトのグループを特定の順序で収容する」ためにのみ使用する必要があります。配列には多くの異なるオブジェクトが含まれていることがあります。これは、「配列の最初の要素は人の名前です」などの規則を覚えるのが難しいため、ユーザーに問題を引き起こす可能性があります。オブジェクトは異なります。コメントを必要とせずに、フィールド名と関数名を使用してそのような情報を伝えることができます。また、オブジェクトを使用すると、「移動関数」を使用して情報をカプセル化し、関連する動作を追加することもできます。
私は以前、次のような構造でデータアップロードシステムを実行しました。

Map<String,String[]> hashMap = new HashMap<String,String[]>();
String[] data = new Data[4];
data[0] = "1";//状态state
data[1] = "1";//延展状态exstate
data[2] = "1";//数值
data[3] = "2020-12-21 23:22:00";//数据生成时间
String key = "01A01";//测点编号
hashMap.put(key,data);
...
for(String key : hashMap.keySet){
    
    
	//状态state、延展状态exstate、数值、数据生成时间
	String[] data = hashMap.get(key);
	String state = data[0];
	String exstate = data[1];
	String value = data[2];
	String time = data[3]'
}

基本データ、リアルタイムデータ、分単位の累積データがデータに含まれているため、この構造のコードプロジェクトはいたるところにあり、記述されたものは不良です。アップロードプログラムは私を苦しめ、振り返ることができませんでした。
"練習"

@Data
public class BasicData(){
    
    
	private int state;
	private int exstate;
	private double value;
	private Date time;
}

Map<String,BasicData> hashMap = new HashMap<String,BasicData>();
BasicData basicData = new BasicData();
basicData.setState = "1";
basicData.setExstate = "1";
basicData.setValue = 3.12;
String str = "2020-12-21 23:22:00";
basicData.setTime = DateUtil.StringToDate(str,"yyyy-MM-dd hh:mm:ss");
String key = "01A01";//测点编号
hashMap.put(key,basicData);
...
for(String key : hashMap.keySet){
    
    
	//状态state、延展状态exstate、数值、数据生成时间
	BasicData basicData = hashMap.get(key);
	String state = basicData.getState();
	String exstate = basicData.getExstate();
	double value = basicData.getValue();
	Date time = basicData.getTime();
}

痛い気づき。
それは実際には本当に簡単です。

/**
 @startTime 2020-12-22 21:30
 @endTime 2020-12-22 22:50
 @startPage 189 
 @endPage 235
 @efficiency 235/7 = 33.6页/天
 @needDays 412/33.6 = 12天
 @overDay 2020-12-16 + 12天 =  2020-12-27
*/

6.「監視データ」をコピーします

GUIコントロールにいくつかのドメインデータがあり、ドメイン機能はこのデータにアクセスする必要があります。データをドメインオブジェクトにコピーします。ドメインオブジェクトとGUIオブジェクトで繰り返されるデータを同期するためのオブザーバーモードを確立します。
「動機付け」
適切に階層化されたシステムは、ユーザーインターフェイスの処理とビジネスロジックの処理のためにコードを分離する必要があります。この理由は次のとおりです。

  1. 同じビジネスロジックを表現するには、異なるユーザーインターフェイスを使用する必要がある場合があります。2つの責任を同時に引き受けると、ユーザーインターフェイスが複雑になりすぎます。
  2. GUIから分離された後、ドメインオブジェクトの保守と進化が容易になり、さまざまな開発者に開発のさまざまな部分を担当させることもできます。

7.一方向の関連付けを双方向の関連付けに変更します

2つのクラスは互いの特性を使用する必要がありますが、それらの間には一方向の接続しかありません。
バックポインタを追加し、変更機能を有効にして2つの接続を同時に更新します。

8.双方向の関連付けを一方向の関連付けに変更します

不要な関連付けを削除します。

9.マジックナンバーをリテラル定数に置き換えます

特別な意味を持つ文字通りの値があります。定数を作成し、その意味に従って名前を付け、上記のリテラル値をこの定数に置き換えます。

10.パッケージフィールド

フィールドをプライベートとしてカプセル化し、対応するアクセス機能を提供します。

11.パッケージコレクション

12.レコードをデータタイプに置き換えます

レコードタイプの構造は、多くのプログラミング環境に共通のプロパティです。一部のレガシープログラムを処理したり、従来のAPIを介してレコード構造と通信したり、データからレコードを読み取ったりする必要がある場合があります。このような場合、これらの外部データを処理するためのインターフェイスクラスを作成する必要があります。これらのデータをJavaBeanに移動します。

13.タイプコードをクラスに置き換えます

14.タイプコードをサブクラスに置き換えます

多態性を使用して、変化する動作を処理します。

15.タイプコードを状態/戦略に置き換えます

クラスの動作に影響を与えるタイプコードがありますが、継承によってそれを排除することはできません。この時点で、タイプコードを状態オブジェクトに置き換えることができます。

16.サブクラスをフィールドに置き換えます

サブクラス間の唯一の違いは、「定数データを返す」関数です。これらの関数を変更して、スーパークラスの新しいフィールドを返すようにしてから、サブクラスを破棄します。

前:「既存のコード1の設計を改善するためのリファクタリング」リファクタリングの原則
次:[高品質のコードの記述:Javaプログラムを改善するための151の提案]

おすすめ

転載: blog.csdn.net/guorui_java/article/details/111434891