You Aren't Gonna Need It • KISS原則 / Keep It Simple, Stupid • OAOO原則 / Once And Only Once • SOLID原則 • 驚き最小の原則 すべての原則を理解して常に 守るようにすれば、どんな場 合でも最高のソフトウェアに なるってことだな!
which states that "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system". By not writing the same information over and over again, our code is more maintainable, more extensible, and less buggy. Getting Started with Rails より
ConsumptionTax; // 消費税・課税対象 } // 商品クラス final readonly class Item { public function getPriceWithTax(Tax $tax): int { return match ($tax) { Tax::TaxFree => $this->price, Tax::ConsumptionTax => (int)floor($this->price * 1.10), }; } } 商品クラス側で match 式を使って計算している
TaxFree { @Override public int amount(final int price) { return 0; } }, // 消費税・課税対象 ConsumptionTax { @Override public int amount(final int price) { return price * rate / 100; } }, public abstract int amount(final int price); }
Int): Int } // 非課税 data class TaxFree : Tax { override fun amount(price: Int): Int = 0 } // 消費税・課税対象 data class ConsumptionTax : Tax { override fun amount(price: Int): Int = (price * 0.1).toInt() }
public function store(Entity $entity): void; public function remove(int $id): void; } // 読み取り専用リポジトリ final readonly class ReadonlyItemRepository implements Repository { public function lookup(int $id): ReadonlyItem { // ... } public function store(Entity $entity): never { throw new BadMethodCallException('not supported'); } public function remove(int $id): never { throw new BadMethodCallException('not supported'); } } 読み取り専用のリポジトリが必要になった
public function store(Entity $entity): void; public function remove(int $id): void; } // 読み取り専用リポジトリ final readonly class ReadonlyItemRepository implements Repository { public function lookup(int $id): ReadonlyItem { // ... } public function store(Entity $entity): never { throw new BadMethodCallException('not supported'); } public function remove(int $id): never { throw new BadMethodCallException('not supported'); } } メソッドの定義が必要なので仕方なく例外を投げる……
} // 書き込み可能なリポジトリのインターフェース interface WritableRepository { public function store(Entity $entity): void; public function remove(int $id): void; } // 一般的なリポジトリ final readonly class ItemRepository implements ReadableRepository, WritableRepository { public function lookup(int $id): Entity { // ... } public function store(Entity $entity): void { // ... } public function remove(int $id): void { // ... } } // 読み取り専用リポジトリ final readonly class ReadOnlyItemRepository implements ReadableRepository { public function lookup(int $id): Entity { // ... } }
} // 書き込み可能なリポジトリのインターフェース interface WritableRepository { public function store(Entity $entity): void; public function remove(int $id): void; } // 一般的なリポジトリ final readonly class ItemRepository implements ReadableRepository, WritableRepository { public function lookup(int $id): Entity { // ... } public function store(Entity $entity): void { // ... } public function remove(int $id): void { // ... } } // 読み取り専用リポジトリ final readonly class ReadOnlyItemRepository implements ReadableRepository { public function lookup(int $id): Entity { // ... } } 用途別に細かくインターフェースを定義する
} // 書き込み可能なリポジトリのインターフェース interface WritableRepository { public function store(Entity $entity): void; public function remove(int $id): void; } // 一般的なリポジトリ final readonly class ItemRepository implements ReadableRepository, WritableRepository { public function lookup(int $id): Entity { // ... } public function store(Entity $entity): void { // ... } public function remove(int $id): void { // ... } } // 読み取り専用リポジトリ final readonly class ReadOnlyItemRepository implements ReadableRepository { public function lookup(int $id): Entity { // ... } } 必要なインターフェースを複数実装する
} // 書き込み可能なリポジトリのインターフェース interface WritableRepository { public function store(Entity $entity): void; public function remove(int $id): void; } // 一般的なリポジトリ final readonly class ItemRepository implements ReadableRepository, WritableRepository { public function lookup(int $id): Entity { // ... } public function store(Entity $entity): void { // ... } public function remove(int $id): void { // ... } } // 読み取り専用リポジトリ final readonly class ReadOnlyItemRepository implements ReadableRepository { public function lookup(int $id): Entity { // ... } } 余分なメソッドの定義がなくなってスッキリ!
readonly class S3FileStorage implements FileStorageInterface { public function __construct( private S3ClientInterface $s3Client ) {} public function upload(UploadedFile $file): void { // S3 へファイルをアップロードする処理 ... } } final readonly class FileUploadService { public function __construct( private FileStorageInterface $storage ) {} public function upload(UploadedFile $file): void { $this->storage->upload($file); // その他アップロードに関する処理 ... } }
readonly class S3FileStorage implements FileStorageInterface { public function __construct( private S3ClientInterface $s3Client ) {} public function upload(UploadedFile $file): void { // S3 へファイルをアップロードする処理 ... } } final readonly class FileUploadService { public function __construct( private FileStorageInterface $storage ) {} public function upload(UploadedFile $file): void { $this->storage->upload($file); // その他アップロードに関する処理 ... } } ファイルを保存したい、というビジネス側の都合で インターフェースを定義する
readonly class S3FileStorage implements FileStorageInterface { public function __construct( private S3ClientInterface $s3Client ) {} public function upload(UploadedFile $file): void { // S3 へファイルをアップロードする処理 ... } } final readonly class FileUploadService { public function __construct( private FileStorageInterface $storage ) {} public function upload(UploadedFile $file): void { $this->storage->upload($file); // その他アップロードに関する処理 ... } } ビジネス側の都合に合わせて技術的な詳細を実装する
readonly class S3FileStorage implements FileStorageInterface { public function __construct( private S3ClientInterface $s3Client ) {} public function upload(UploadedFile $file): void { // S3 へファイルをアップロードする処理 ... } } final readonly class FileUploadService { public function __construct( private FileStorageInterface $storage ) {} public function upload(UploadedFile $file): void { $this->storage->upload($file); // その他アップロードに関する処理 ... } } ビジネスロジックから技術的な詳細への依存が消えた