No Programming, No Life

プログラミング関連の話題や雑記

Re:業務アプリの業務部分で、オブジェクト指向なんか使わないよね(その2)

前回の記事の続きです。

こちらにて、id:kmaebashiさんにお返事をいただいたので、ちょっとだけ具体的な例を考えてみました。
あまりいいものがパッと思いつかなかったのですが一応書いておきます。id:kmaebashiさんの例と同じ、はてなダイアリーについて。

はてなダイアリーにおけるER関係(引用)

  • 1「ユーザ」に対し、n「アカウント」(サブアカウントとやらがあるらしいので)
  • 1「アカウント」に対し、1「ダイアリ」
  • 1「ダイアリ」に対し、n「日記記事」(「日記記事」は1日分の記事を表現する)
  • 1「日記記事」に対し、n「エントリ」
業務アプリの業務部分で、オブジェクト指向なんか使わないよね - プログラミング言語を作る日記

ということで、以下のようなクラスを想定

/** ユーザを表現するクラス */
class User {
	public String id;       // ユーザID
	public String password; // パスワード
	public Account getMainAccount()          { ...(省略)... }
	public Account getSubAccount(String id)  { ...(省略)... }
}

/** アカウントを表現するクラス */
class Account {
	/** 新規投稿する */
	public void entry(String contents)       { ...(省略)... }
		:
		:
		:
}

/** 1日単位のダイアリーを表現するクラス */
class Diary {
	private String      title;   // タイトル
	private List<Entry> entries; // エントリー
		:
		:
		:
}

/** エントリを表現するクラス */
class Entry {
	private String       contents; // 内容
	private List<String> comments; // コメント
	private boolean      isTemp;   // 一時保存かどうか

	/** エントリに編集する */
	public void editContents() { ...(省略)... }
	/** コメントを編集する */
	public void editComments() { ...(省略)... }
	/** 永続化する */
	public void commit()       { ...(省略)... }
		:
		:
		:
}

/** ビジネスロジックの親クラス */
abstract class Action {
	/** アクションを実行 */
	public void perform() {
		// ルールは守られているかチェック
		checkBusinessRule();
		// ロジックを実行
		doAction();
	}

	/** 業務ルールチェック処理:実装は子クラスに任せた */
	public abstract void checkBusinessRule();
	/** 実際のアクション処理:実装は子クラスに任せた */
	public abstract void doAction();
		:
		:
		:
}

/** 新規投稿するビジネスロジック */
class NewEntryAction extends Action {
	/** 業務ルールをチェック */
	public void checkBusinessRule() throws Exception {
		// 0時から6時は投稿禁止!
		if (0 <= hour && hour <= 6) {
			throw new Exception("時間外の投稿は禁止です");
		}
	}

	/** 新規投稿する */
	public void doAction(Account account, int hour) {
		account.entry("投稿する内容");
	}
		:
		:
		:
}

擬似コードのため、引数などは適当です

解説

上記のような構造のクラスを定義した場合、例えば新規投稿するビジネスロジックですべきことは業務ルールが守られているかのチェックメソッドの実装(checkBusinessRule)と、新規投稿時の処理内容の実装(doAction)です。
この例では新規投稿ですので、doActionメソッドはAccount#entryメソッドを呼んでいるだけです。この他、編集する処理、削除する処理など、必要な処理分、Actionのサブクラスを作っていくことになります。それぞれの処理によって、業務ルールが違う場合は、checkBusinessRuleの実装を変えればいいでしょうし、DBに対する操作が違うのであれば、Account#entryメソッドではなく、違うメソッドを呼ぶように実装します。*1

Account#entryメソッドの中の処理としては、DiaryからEntryのリストを取得してきて、Entryのedit系メソッドを使って内容を編集し、最後にcommitメソッドを呼んで永続化するといった感じになるでしょうか。ここら辺でO/Rマッパーが使えるかもしれませんね。

このように処理を分離することで、プログラマは、例えば0時から6時は投稿禁止であるといった業務処理部分のみ集中して書くことができるようになり、プログラムの見通しがよくなります。もしこのチェックロジックの中に突如としてSQL文が登場してきたら読みにくいですよね。

以下、自己つっこみ

ちなみにこれらの処理を効率よくやってくれるのがフレームワーク。このくらいの規模だとちょっと冗長な感じがする。なんだかオブジェクト指向論というよりも、処理の分離、仕事の分担が大事だよという方向性になってきている気がする。
場合によっては十分見通しが良ければオブジェクト指向しないという選択もアリなのかもしれない。

*1:ここで使っているのはテンプレートメソッドパターン。