とりあえず雑記帳(跡地)
そしてフレームワークへ
最終更新:
fujiyan
-
view
1箇所たりとも修正したくない
先のBinaryOperationは、少ないとはいえ、やはり修正している事実に変わりはありません。
「修正せずに拡張する」の精神にのっとり、さらに工夫をしてみましょう。
「修正せずに拡張する」の精神にのっとり、さらに工夫をしてみましょう。
演算の追加を、BinaryOperation自身で行わずに、外部から演算オブジェクトを
追加するように変更することで、BinaryOperationの本質的な部分は一切変更せずに拡張できるようにします。
追加するように変更することで、BinaryOperationの本質的な部分は一切変更せずに拡張できるようにします。
package jp.fujiyan.binaryoperation5;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* 二項演算フレームワークです。
*
* @author fujiyan
*/
public class BinaryOperationFramework {
// 入力値の最小
private static final int MIN_VALUE = -99999;
// 入力値の最大
private static final int MAX_VALUE = 99999;
// 選択肢
private static final int FINISH = 0;
// 演算子のリスト
private List<IBinaryOperator> operatorList = new ArrayList<IBinaryOperator>();
/**
* ユーザ入力を返します。
*
* @param prompt プロンプト文
* @return ユーザ入力文字列
*/
private static String input(String prompt) throws IOException {
System.out.println(prompt);
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = in.readLine();
if (str == null) {
// Ctrl+Zによる中断
throw new EOFException("中断されました。");
}
return str;
}
/**
* 整数値を問い合わせます。
*
* @return 整数値
*/
private static int askValue(int seq) throws IOException {
int value = 0;
boolean invalid = true;
while (invalid) {
// 整数値が入力されるまで
String ret = input(String.valueOf(seq) + "番目の数値を入力してください(" + MIN_VALUE + "~" + MAX_VALUE + "の整数)。");
try {
value = Integer.parseInt(ret);
} catch (NumberFormatException e) {
// 整数値として有効でない文字列
System.out.println("入力された値は整数値ではありません。");
continue;
}
invalid = (value < MIN_VALUE) || (value > MAX_VALUE);
if (invalid) {
// 無効な入力
System.out.println("入力された数値が範囲を超えています。");
}
}
return value;
}
/**
* 演算子を追加します。
*
* @param operator 演算子
*/
public void addOperator(IBinaryOperator operator) {
operatorList.add(operator);
}
/**
* 操作を問い合わせます。
*
* @return 操作の選択肢
*/
private int askOperation() throws IOException {
StringBuilder sb = new StringBuilder();
int i = 0;
sb.append(i).append(":").append("終了");
while (i < operatorList.size()) {
sb.append(" ");
sb.append(i + 1).append(":").append(operatorList.get(i).getChoiseString());
i++;
}
String prompt = "操作を番号で選択してください。(" + new String(sb) + ")";
int operation = 0;
boolean invalid = true;
while (invalid) {
// 有効な選択肢が入力されるまで
String ret = input(prompt);
try {
operation = Integer.parseInt(ret);
invalid = (operation < 0) || (operation > operatorList.size());
} catch (NumberFormatException e) {
// 数値として有効でない文字列
}
if (invalid) {
// 無効な入力
System.out.println("正しい番号を選択してください。");
}
}
return operation;
}
/**
* 演算を行います。
*
* @param operation 操作の選択肢
* @param value1 値1
* @param value2 値2
*/
private void execute(int operation, int value1, int value2) {
IBinaryOperator op = operatorList.get(operation - 1);
op.calculate(value1, value2);
if (op.isError()) {
// エラーあり
System.out.println(op.getErrorMessage());
return;
}
System.out.println(op.getExpression() + " の答えは " + op.getResult() + " です。");
}
/**
* 計算を実施します。
*/
public void perform() {
try {
int operation = askOperation();
while (operation != FINISH) {
// "終了"が選択されるまで
int val1 = askValue(1);
int val2 = askValue(2);
execute(operation, val1, val2);
operation = askOperation();
}
System.out.println("終了します。");
} catch (EOFException e) {
// Ctrl+Zによる中断
System.err.println(e.getMessage());
} catch (Exception e) {
// その他例外
System.err.println("なんかまずいことが発生しました。");
e.printStackTrace();
}
}
}
メインが演算を追加する
/*
* オブジェクト指向版
* フレームワーク化
*/
package jp.fujiyan.binaryoperation5;
import jp.fujiyan.binaryoperation5.operator.Addition;
import jp.fujiyan.binaryoperation5.operator.Division;
import jp.fujiyan.binaryoperation5.operator.Multiplication;
import jp.fujiyan.binaryoperation5.operator.Power;
import jp.fujiyan.binaryoperation5.operator.Subtraction;
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
BinaryOperationFramework framework = new BinaryOperationFramework();
framework.addOperator(new Addition());
framework.addOperator(new Subtraction());
framework.addOperator(new Multiplication());
framework.addOperator(new Division());
framework.addOperator(new Power());//$$$$$修正$$$$$
framework.perform();
}
}
※他のクラスは変更なし
ココまで来ると、いよいよ「二項演算フレームワーク」として、演算の追加には、計算処理を扱うクラス(BinaryOperationFramework)は一切変更しなくてすむようになりました。
フレームワークには、3つの要素が出現します。
- フレームワーク(BinaryOperationFramework、IBinaryOperator、StandardOperator)
全体の処理を流れを定義します(BinaryOperationFramework)。拡張部分をインターフェースとして提供します(IBinaryOperator)。場合によっては、実装を補助する基底クラスも提供します(StandardOperator)。
- プラグイン(Addition、Subtraction、Multiplication、Division、Power)
フレームワークから提供されたインターフェースを実装したクラスです。フレームワークに処理させたい内容を実装します。
- コントローラ(Main)
フレームワークとプラグインを結びつけます。
StrutsやSpring等のフレームワークでは、コントローラの処理を外部ファイルで記述させたり、命名規則で制御することで、プログラムの再コンパイルを不要にしています。
StrutsやSpring等のフレームワークでは、コントローラの処理を外部ファイルで記述させたり、命名規則で制御することで、プログラムの再コンパイルを不要にしています。
…と、えらく大雑把で駆け足でしたが、インターフェースの使い道がお伝えできたでしょうか。
「インターフェースを活用する=フレームワークを作る」と言ってもいいでしょう。
ぜひぜひ、いろんなフレームワークを作ってみてください。
ぜひぜひ、いろんなフレームワークを作ってみてください。
(了)