SpringとUMLを活用したチケット販売機システムの設計実装

概要

UML(統一モデリング言語)は、オブジェクト指向設計の可視化に役立ちます。本記事では、UMLクラス図を用いてチケット販売機の部品をモデル化し、Spring FrameworkのDI(依存性注入)とAOP(アスペクト指向プログラミング)を組み合わせて実装する方法を解説します。

UMLによる部品設計

販売機の各ハードウェア部品は、共通の抽象クラスBaseComponentを継承します。このクラスは自己診断(runSelfCheck())と初期化(reset())の抽象メソッドを持ちます。

// BaseComponent.java
public abstract class BaseComponent {
    String statusMessage;

    public BaseComponent() {}
    public BaseComponent(String msg) { this.statusMessage = msg; }

    abstract void reset();
    abstract void runSelfCheck();
}

キーボードは抽象クラスKeypadとして定義し、押下されたキーを取得するgetPressedKey()を持ちます。具体的なキーボードとして、以下の3つを実装します。

  • ConfirmKeypad – 確認・キャンセルキー
  • ZoneKeypad – 目的地キー
  • SeatTypeKeypad – 座席種別キー
// Keypad.java
public abstract class Keypad extends BaseComponent {
    abstract int getPressedKey();
}

// ConfirmKeypad.java
public class ConfirmKeypad extends Keypad {
    private int inputKey;
    public ConfirmKeypad(int key) { this.inputKey = key; }
    @Override int getPressedKey() { return inputKey; }
    @Override void reset() { /* リセット処理 */ }
    @Override void runSelfCheck() { /* 自己診断 */ }
    void handleConfirmAction() { /* イベント処理 */ }
}

画面表示はDisplayクラス、現金投入口はCoinSlotPaymentインターフェース実装)、カードリーダーはCardReader(同インターフェース実装)とします。プリンターはTicketPrinterクラスです。

// Payment.java
public interface Payment {
    int getAmount();
}

// CoinSlot.java
public class CoinSlot extends BaseComponent implements Payment {
    private int amount;
    public CoinSlot(int a) { this.amount = a; }
    @Override public int getAmount() { return amount; }
    // その他メソッド
}

// CardReader.java
public class CardReader extends BaseComponent implements Payment {
    @Override public int getAmount() { return 0; }
    String readCredit() { return null; }
    double deductFare() { return 0.0; }
    void ejectCard() {}
}

Spring DIによる部品組み立て

販売機の制御クラスTicketVendingMachineは、コンストラクタインジェクションで各キーボードと支払い手段を受け取ります。SpringのXML設定でBeanを定義し、依存関係を解決します。

// TicketVendingMachine.java
public class TicketVendingMachine {
    private ZoneKeypad zoneKeypad;
    private SeatTypeKeypad seatKeypad;
    private ConfirmKeypad confirmKeypad;
    private Payment paymentMethod;

    public TicketVendingMachine(ZoneKeypad z, SeatTypeKeypad s,
                                ConfirmKeypad c, Payment p) {
        this.zoneKeypad = z;
        this.seatKeypad = s;
        this.confirmKeypad = c;
        this.paymentMethod = p;
    }

    public boolean validatePayment() {
        // 料金計算と支払い検証のロジック(簡略化)
        int unitPrice = PriceDB.getPrice(
            zoneKeypad.getPressedKey(),
            seatKeypad.getPressedKey());
        int total = unitPrice * confirmKeypad.getPressedKey();
        return total == paymentMethod.getAmount();
    }

    public void processSale() {
        if (validatePayment()) {
            TicketPrinter printer = new TicketPrinter();
            printer.printTicket();
            printer.ejectTicket();
        }
    }
}

Spring設定ファイル(beans.xml)では、各BeanとAOPアドバイスを定義します。AOPを使用して、画面表示の前後処理をDisplayクラスのメソッドで実行します。

<!-- beans.xml -->
<beans>
    <bean id="zoneKeypad" class="com.example.ZoneKeypad">
        <constructor-arg value="578"/>
    </bean>
    <bean id="seatKeypad" class="com.example.SeatTypeKeypad">
        <constructor-arg value="1"/>
    </bean>
    <bean id="confirmKeypad" class="com.example.ConfirmKeypad">
        <constructor-arg value="2"/>
    </bean>
    <bean id="payment" class="com.example.CoinSlot">
        <constructor-arg value="318"/>
    </bean>

    <bean id="machine" class="com.example.TicketVendingMachine">
        <constructor-arg name="zoneKeypad" ref="zoneKeypad"/>
        <constructor-arg name="seatKeypad" ref="seatKeypad"/>
        <constructor-arg name="confirmKeypad" ref="confirmKeypad"/>
        <constructor-arg name="paymentMethod" ref="payment"/>
    </bean>

    <bean id="display" class="com.example.Display">
        <constructor-arg value="318"/>
    </bean>

    <aop:config>
        <aop:aspect ref="display">
            <aop:pointcut id="salePointcut"
                expression="execution(* com.example.TicketVendingMachine.*(..))"/>
            <aop:before method="showBefore" pointcut-ref="salePointcut"/>
            <aop:after method="showAfter" pointcut-ref="salePointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

メインクラスではClassPathXmlApplicationContextを使ってSpringコンテキストを起動し、TicketVendingMachineのBeanを取得して販売処理を実行します。

// Main.java
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx =
            new ClassPathXmlApplicationContext("beans.xml");
        TicketVendingMachine machine = ctx.getBean(TicketVendingMachine.class);
        machine.processSale();
    }
}

まとめ

UMLで部品の抽象化と継承関係を明確にし、SpringのDIで部品を柔軟に組み替えられる設計にしました。AOPを利用することで、画面表示などの横断的関心事を本体ロジックから分離しています。このアプローチは、実機のハードウェア交換や表示ロジックの変更にも容易に対応できる拡張性を提供します。

タグ: Spring UML DI AOP Java

8月1日 00:59 投稿