📘

ソフトウェアデザインやアーキテクチャの学び方

2023/10/08に公開

この記事を元に気になったことを調べた

Clean Code

関数に真偽値を渡すな

https://alexkondov.com/should-you-pass-boolean-to-functions/

関数は一つのことをすべきだというsinle responsibility principleがあると思うけど、真偽値を渡すことによって、反対のことをさせることになる。
フラグは、その関数が使用されるコンテキストに基づいて、関数の動作を変更するために使用されます。これは、関数が複数のことを行っていて、まとまっていないことを示す良いシグナルである。

CQS

https://ericbackhage.net/c/cleaner-code-with-command-query-separation/

クエリーは、システム(の一部)の現在の状態に関する情報を返すメソッドである。システムの状態を変更することはできない。したがって、QueryメソッドはCommandメソッドを呼び出してはならない。
本当に貴重な利点は、システムの状態を変更するメソッドとそうでないメソッドを簡単に確認できることだ。システムの状態を変更しないメソッドは、必要なときにいつでもアレンジして呼び出すことができるし、システムの状態を変更するメソッドは、より注意深く扱わなければならない。こうすることで、コードを扱いやすくなり、異なるシナリオに対してシステムで何が起こるかを理解しやすくなる。

意味のある命名

clean code

変数、関数、クラス名はそれがなぜ存在するのか、何をするのか、どのように使用されるか、など意図を明確にする必要がある

純粋関数

純粋関数とは、引数を受け取り、引数に基づいて値を計算し、値を返す関数のこと
純粋関数は常に少なくとも1つの引数を受け取らなければならない。この引数は変更されずに残りますが、関数本体の内部で行われる計算で使用されます。さらに、必ず戻り値がなければならない。
純粋関数のしないこと: 渡された引数を変更したり、アプリケーションの状態を変更したり、グローバル変数を設定/更新したり、フェッチ要求をトリガーするような副作用を引き起こしてはなりません
純粋関数のメリット: 純粋関数はプログラムのエコシステムから独立しているので、デバッグやリファクタリングがしやすい。同じ入力があれば同じ出力が期待できる。そのため、これらの関数に対して簡単なテストを書くことが期待でき、問題が発生してもすぐに診断することができる。また、これらの純粋な関数は副作用を引き起こさないので、他のバグの原因として除外することもできる。副作用がないことのもうひとつの利点は、純粋関数をリファクタリングするときに、誤ってプログラムの別の部分をいじってしまうかもしれないという不安を払拭できること。これらの関数をリファクタリングしても、プログラム内の他の関数が変更されることはない。

クラスのためのswitchのリファクタ

正しい構造体を使う

https://softinbit.medium.com/building-strong-foundations-the-importance-of-using-correct-constructs-in-clean-code-principles-29f715f34f3e

正しい構造体を使うことで、効率的でメンテナンスしやすくなる
配列だったらarrayを使えばsortとか使える(→適切な構造体を使う)

using System;

public class Program
{
    public static void Main()
    {
        string[] names = { "Alice", "Bob", "Charlie", "Dave", "Eve" };
        Array.Sort(names);
        Console.WriteLine("Sorted names:");
        foreach (string name in names)
        {
            Console.WriteLine(name);
        }
    }
}

switch文をクラスにリファクタしろ

clean code
のswitch文の章を抜粋しました

悪い例

Employeeという1つクラスに様々な関数を持たせてswitch文でその契約形態ごとに分岐させている

public Money calculatePay(Employee e) throws InvalidEmployeeType{
 switch(e.type){
  case COMMISSIONED: return calculateCommissionedPay(e);
  case HOURLY: return calculateHourlyPay(e);
  case SALARIED: return calculateSalariedPay(e);
  default: throw new InvalidEmployeeType(e.type);
   }
  }

良い例

Employeeを1つ作るのではなく、ファクトリメソッドでCommissionedEmployee、HourlyEmployee、SalariedEmployeと適切なクラスを作成し、それぞれに必要な分だけのメソッドを実装する

public abstract classEmployee{
 public abstract boolean isPayday();
 public abstrac tMoneycalculatePay();
 public abstract void deliverPay(Moneypay);
}

------------------
public interfaceEmployeeFactory{
  public Employee makeEmployee(EmployeeRecord r) throws 
  InvalidEmployeeType;
 }
------------------
public class EmployeeFactory Implimplements EmployeeFactory{
 public EmployeemakeEmployee(EmployeeRecord r) throws
  InvalidEmployeeType{
   switch(r.type){
    case COMMISSIONED: return new CommissionedEmployee(r);
    case HOURLY: return new HourlyEmployee(r);
     case SALARIED: return new SalariedEmploye(r);
     default: throw new InvalidEmployeeType(r.type);
	}
}

フレームワークコードと距離を取れ

http://blog.a-way-out.net/blog/2020/12/25/how-to-reduce-dependency-on-frameworks/

フレームワークのコードは変更しづらい
それほど安定しておらずバージョンもどんどん上がる

Discussion