はじめに
- 韓国人として、日本語とコンピュータの勉強を同時に行うために、ここに文章を書いています
- 翻訳ツールの助けを借りて書いた文章なので、誤りがあるかもしれません
開発の勉強をしていると、時折こんな考えが浮かんできます。
「良いコードとは何だろうか?」
私自身、良いコードとは次のようなものだと考えています。
「修正は閉じられていて、拡張には開かれているコード」
これは オープン・クローズド原則(OCP: Open-Closed Principle) とも関連しています。私たちは常に変化する「ソフト」ウェアを作っているため、コードの柔軟性と拡張性は非常に重要です。
今回は「密結合」と「疎結合」について、簡単に見ていきたいと思います。
密結合 (Tight Coupling)
まず、以下のような飲み物の自動販売機を作ってみました。(例として、できるだけシンプルに記述しています。)
public class Coke {
public void dispense(){
System.out.println("cola come out");
}
}
public class Coffee {
public void dispense(){
System.out.println("coffee come out");
}
}
public class DrinkMachine {
private Coke coke;
private Coffee coffee;
public DrinkMachine() {
coke = new Coke();
coffee = new Coffee();
}
public void dispenseCoke() {
coke.dispense();
}
public void dispenseCoffee() {
coffee.dispense();
}
}
このコードを見ると、自動販売機はCoffeeとCokeに直接依存していることがわかります。
また、CokeとCoffeeのオブジェクトを直接生成しています。
ここで、新しい飲み物としてPepsiを追加するとしましょう。
public class Pepsi{
public void dispense(){
System.out.println("pepsi coke come out");
}
}
その場合、DrinkMachineクラスは次のように変更する必要があります。
public class DrinkMachine {
private Coke coke;
private Coffee coffee;
private Pepsi pepsi; // 新しいフィールドの追加
public DrinkMachine() {
coke = new Coke();
coffee = new Coffee();
pepsi = new Pepsi(); // 新しいオブジェクトを生成
}
//...
public void dispensePepsi() {
pepsi.dispense(); // 新しいオブジェクトを呼び出す
}
}
もし飲み物が1種類ではなく、20種類、30種類...n種類と追加されると仮定した場合、どうなるでしょうか?
新しい飲み物を追加するたびに、自動販売機であるDrinkMachineクラスを修正しなければならない状況が発生してしまいます。
これは「密結合」の例であり、変更が発生するたびに内部コードを修正する必要があるため、コードの柔軟性と拡張性を妨げる要因となります。
疎結合 (Loose Coupling)
疎結合とは?
In computing and systems design, a loosely coupled system is one
- in which components are weakly associated (have breakable relationships) with each other, and thus changes in one component least affect existence or performance of another component
- in which each of its components has, or makes use of, little or no knowledge of the definitions of other separate components. Subareas include the coupling of classes, interfaces, data, and services.Loose coupling is the opposite of tight coupling
疎結合Loose Couplingとは、コンピューティングやシステム設計において、以下のような特徴を持つシステムを指します。
- 構成要素間の弱い関連性: 構成要素間の関係が弱く、特定の構成要素が変更されても、他の構成要素の存在や性能に最小限の影響しか与えません
- 構成要素の独立性: 各構成要素が他の構成要素の定義や動作についてほとんど、またはまったく知らない状態を意味します
疎結合は、密結合(Tight Coupling) の反対概念です。
それでは、具体的なコードを見ながら疎結合の実装方法を確認していきましょう。まず、すべての飲み物に共通のインターフェースを以下のように定義します:
public interface Drink {
void dispense();
}
public class Coke implements Drink {
public void dispense(){
System.out.println("cola come out");
}
}
public class Coffee implements Drink {
public void dispense(){
System.out.println("coffee come out");
}
}
まず、次のように共通のインターフェースを定義します。
public class DrinkMachine {
private Map<String, Drink> drinks;
public DrinkMachine() {
drinks = new HashMap<>();
}
// 飲み物を追加するメソッド
public void addBeverage(String name, Drink beverage) {
drinks.put(name, beverage);
}
// 飲み物を選択するメソッド
public void selectBeverage(String name) {
Drink drink = drinks.get(name);
if (drink != null) {
drink.dispense();
} else {
System.out.println("該当する飲み物がありません。");
}
}
}
DrinkMachineクラスは飲み物の管理と選択に責任を持っています。飲み物はMapを使って動的に追加・管理されます。
以上のように、インターフェースを使用することで、飲み物の追加や変更が容易になりました。この実装方法について、もう少し詳しく見ていきましょう。
では、実際にPepsiを追加する場合を考えてみましょう。新しい飲み物を追加するのに必要な手順は非常にシンプルです
public class Pepsi implements Drink {
public void dispense(){
System.out.println("pepsi coke come out");
}
}
次のように追加する飲み物を作成し、
public class Main {
public static void main(String[] args) {
DrinkMachine machine = new DrinkMachine();
// 飲み物を追加
machine.addBeverage("cola", new Coke());
machine.addBeverage("coffee", new Coffee());
machine.addBeverage("pepsi", new Pepsi());
次のように単純に自動販売機に登録するだけで完了します。
以前の密結合のコードとの違いは、飲み物(オブジェクト)を生成および選択する責任が自動販売機にはなくなった点です。
簡単に言えば、新しい飲み物を追加してもDrinkMachineを修正する必要がなくなりました。
このように、疎結合を実現することで、コードの柔軟性と拡張性を向上させることができました。
-
依存性逆転の原則 (DIP)
- 実装ではなく抽象に依存することで、モジュール間の結合度を下げ、柔軟性を高める
- この例では、DrinkMachineクラスがDrinkという抽象(インターフェース)に依存することで、飲み物クラスの変更の影響を受けないようにしています
-
オープン・クローズド原則 (OCP)
- 拡張には開かれており、修正には閉じられているべきです
- 新しい飲み物を追加するには、Drinkインターフェースを実装するクラスを作成し、それをDrinkMachineに登録するだけで済みます
したがって、DrinkMachine自体のコードを修正する必要がなく、OCPを満たしています。
その他の内容
疎結合と密結合の概念は、アーキテクチャレベルでも広く使用されており、一種のパラダイムと言えるでしょう。
すべては、「変更には閉じられており、拡張には開かれている」ソフトウェアを作るために採るべきアプローチではないかと考えます。