GRASP (General Responsibility Assignment Software Patterns) と C#

GRASP (General Responsibility Assignment Software Patterns) は、オブジェクト指向設計における責任の分配に関する指針を提供する重要な原則群です。C# を使用したプログラミングに GRASP を適用することで、保守性や拡張性の高いソフトウェアを設計できます。本ドキュメントでは、GRASP の 9 つの原則を、それぞれの概要、C# の具体例、対応するクラス図とともに解説します。


1. Information Expert (情報エキスパート)

責任を持つべきオブジェクトを決定する際、関連する情報を最も多く持つオブジェクトに割り当てます。

C# の例

public class Order
{
    private List<OrderLine> _orderLines = new List<OrderLine>();

    public decimal CalculateTotal()
    {
        return _orderLines.Sum(line => line.Price);
    }
}

public class OrderLine
{
    public decimal Price { get; set; }
}

この例では、Order クラスが計算責任を持つのは、OrderLine の情報を保持しているからです。

PlantUML クラス図

@startuml
class Order {
    - List<OrderLine> _orderLines
    + CalculateTotal() : decimal
}

class OrderLine {
    + Price : decimal
}

Order --> OrderLine : has
@enduml

2. Creator (生成者)

あるオブジェクトが別のオブジェクトを生成するに適しているかどうかを判断します。

C# の例

public class Customer
{
    public Order CreateOrder()
    {
        return new Order();
    }
}

CustomerOrder を生成する責任を持つ適切なオブジェクトです。これは、OrderCustomer に密接に関連しているためです。

PlantUML クラス図

@startuml
class Customer {
    + CreateOrder() : Order
}

class Order

Customer --> Order : creates
@enduml

3. Controller (コントローラー)

システム操作を受け取り、その操作を適切なオブジェクトにデリゲートする責任を持つオブジェクトを選びます。

C# の例

public class OrderController
{
    public void ProcessOrder()
    {
        Order order = new Order();
        order.AddLineItem(new OrderLine { Price = 10 });
        order.AddLineItem(new OrderLine { Price = 20 });

        Console.WriteLine($"Total: {order.CalculateTotal()}");
    }
}

この例では、OrderController がユーザー操作を受け取り、注文を処理します。

PlantUML クラス図

@startuml
class OrderController {
    + ProcessOrder() : void
}

class Order {
    + AddLineItem(line : OrderLine) : void
    + CalculateTotal() : decimal
}

class OrderLine {
    + Price : decimal
}

OrderController --> Order : uses
Order --> OrderLine : has
@enduml

4. Low Coupling (低結合)

オブジェクト間の依存関係を最小限にする設計を目指します。

C# の例

public class PaymentProcessor
{
    public void ProcessPayment(IPaymentMethod paymentMethod)
    {
        paymentMethod.Pay();
    }
}

public interface IPaymentMethod
{
    void Pay();
}

public class CreditCardPayment : IPaymentMethod
{
    public void Pay()
    {
        Console.WriteLine("Paying with credit card.");
    }
}

public class PayPalPayment : IPaymentMethod
{
    public void Pay()
    {
        Console.WriteLine("Paying with PayPal.");
    }
}

PaymentProcessorIPaymentMethod を通じて支払いを処理し、低結合を実現しています。

PlantUML クラス図

@startuml
class PaymentProcessor {
    + ProcessPayment(paymentMethod : IPaymentMethod) : void
}

interface IPaymentMethod {
    + Pay() : void
}

class CreditCardPayment {
    + Pay() : void
}

class PayPalPayment {
    + Pay() : void
}

PaymentProcessor --> IPaymentMethod : uses
IPaymentMethod <|.. CreditCardPayment
IPaymentMethod <|.. PayPalPayment
@enduml

5. High Cohesion (高凝集)

関連する責任を1つのクラスに集中させ、役割を明確にします。

C# の例

public class Inventory
{
    private List<Item> _items = new List<Item>();

    public void AddItem(Item item)
    {
        _items.Add(item);
    }

    public bool IsInStock(string itemName)
    {
        return _items.Any(item => item.Name == itemName);
    }
}

public class Item
{
    public string Name { get; set; }
}

この例では、Inventory クラスが在庫管理の責任を持っています。

PlantUML クラス図

@startuml
class Inventory {
    - List<Item> _items
    + AddItem(item : Item) : void
    + IsInStock(itemName : string) : bool
}

class Item {
    + Name : string
}

Inventory --> Item : manages
@enduml

6. Polymorphism (ポリモーフィズム)

動作を異なるオブジェクトタイプに応じて変える場合にポリモーフィズムを活用します。

C# の例

public abstract class Shape
{
    public abstract double CalculateArea();
}

public class Circle : Shape
{
    public double Radius { get; set; }
    public override double CalculateArea() => Math.PI * Radius * Radius;
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    public override double CalculateArea() => Width * Height;
}

Shape クラスを基に、それぞれの図形クラスが独自の面積計算を実装しています。

PlantUML クラス図

@startuml
abstract class Shape {
    + CalculateArea() : double
}

class Circle {
    + Radius : double
    + CalculateArea() : double
}

class Rectangle {
    + Width : double
    + Height : double
    + CalculateArea() : double
}

Shape <|-- Circle
Shape <|-- Rectangle
@enduml

7. Pure Fabrication (純粋な作り物)

問題領域に直接関係しないが、設計を簡略化するために役立つクラスを作成します。

C# の例

public class Logger
{
    public void Log(string message)
    {
        Console.WriteLine($"Log: {message}");
    }
}

Logger クラスは、設計上の補助的な役割を担っています。

PlantUML クラス図

@startuml
class Logger {
    + Log(message : string) : void
}
@enduml

8. Indirection (間接化)

直接的な結合を避けるために、間接的なオブジェクトを導入します。

C# の例

public class Database
{
    public void Save(string data)
    {
        Console.WriteLine($"Saving {data}");
    }
}

public class Repository
{
    private Database _database;

    public Repository(Database database)
    {
        _database = database;
    }

    public void SaveData(string data)
    {
        _database.Save(data);
    }
}

この例では、Repository を通じてデータベースアクセスを間接化しています。

PlantUML クラス図

@startuml
class Database {
    + Save(data : string) : void
}

class Repository {
    - Database _database
    + SaveData(data : string) : void
}

Repository --> Database : uses
@enduml

9. Protected Variations (保護変動)

変動しやすい部分を保護するためにインターフェースや抽象クラスを活用します。

C# の例

public interface INotifier
{
    void Notify(string message);
}

public class EmailNotifier : INotifier
{
    public void Notify(string message)
    {
        Console.WriteLine($"Sending email: {message}");
    }
}

public class SmsNotifier : INotifier
{
    public void Notify(string message)
    {
        Console.WriteLine($"Sending SMS: {message}");
    }
}

public class NotificationService
{
    private INotifier _notifier;

    public NotificationService(INotifier notifier)
    {
        _notifier = notifier;
    }

    public void NotifyUser(string message)
    {
        _notifier.Notify(message);
    }
}

インターフェースを使用して、柔軟性を高めています。

PlantUML クラス図

@startuml
interface INotifier {
    + Notify(message : string) : void
}

class EmailNotifier {
    + Notify(message : string) : void
}

class SmsNotifier {
    + Notify(message : string) : void
}

class NotificationService {
    - INotifier _notifier
    + NotifyUser(message : string) : void
}

INotifier <|.. EmailNotifier
INotifier <|.. SmsNotifier
NotificationService --> INotifier : uses
@enduml

結論

GRASP の原則を C# に適用することで、ソフトウェアの保守性、拡張性、再利用性を向上させることができます。これらの原則を実際のプロジェクトに適用する際には、それぞれの利点とトレードオフを慎重に考慮してください。