迭代1:快速實(shí)現(xiàn)
需求1:實(shí)現(xiàn)一個(gè)計(jì)算器,完成加減乘除運(yùn)算
public final class Operations {
public final static int PLUS = 0;
public final static int MINUS = 1;
public final static int TIMES = 2;
public final static int DIVIDE = 3;
public static int apply(int op, int x, int y) {
switch (op) {
case PLUS: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
case DIVIDE: return x / y;
}
throw new AssertionError("Unknown op: " + op);
}
private Operations() {
}
}
這是一個(gè)很糟糕的設(shè)計(jì)晤揣,存在很多的壞味道屋确。
類型不安全
一般地纳击,用戶按照規(guī)則傳遞正確的op
常量续扔。
import static cn.codingstyle.Operations.*;
assertThat(apply(PLUS, 1, 1), equalTo(2));
但不排除用戶傳遞錯(cuò)誤的op
值,導(dǎo)致運(yùn)行時(shí)失敗焕数,拋出AssertionError
纱昧。
int op = ...
assertThat(apply(op, 1, 1), equalTo(2));
違背OCP原則
對于增加新的操作類型,或者增加行為堡赔,將導(dǎo)致大量的重復(fù)代碼识脆,給后期維護(hù)帶來很大的傷害。
需求2:輸出操作符的字符串表示
可以增加operator
方法善已,用于輸出操作符表示的字符串灼捂,設(shè)計(jì)存在大量重復(fù)。
public static int apply(int op, int x, int y) {
switch (op) {
case PLUS: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
case DIVIDE: return x / y;
}
throw new AssertionError("Unknown op: " + op);
}
public static String operator(int op) {
switch (op) {
case PLUS: return "+";
case MINUS: return "-";
case TIMES: return "*";
case DIVIDE: return "/";
}
throw new AssertionError("Unknown op: " + op);
}
需求3:支持求模運(yùn)算
每況愈下换团,如果再增加一個(gè)求模數(shù)的操作類型悉稠,則需要散彈式地修改程序。
public static int apply(int op, int x, int y) {
switch (op) {
case PLUS: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
case DIVIDE: return x / y;
case MOD: return x % y;
}
throw new AssertionError("Unknown op: " + op);
}
public static String operator(int op) {
switch (op) {
case PLUS: return "+";
case MINUS: return "-";
case TIMES: return "*";
case DIVIDE: return "/";
case MOD: return "%";
}
throw new AssertionError("Unknown op: " + op);
}
當(dāng)需求變化時(shí)艘包,應(yīng)遵循良好的正交設(shè)計(jì)原則的猛,避免做乘法,只做加法辑甜,甚至是減法衰絮。
迭代2:類型安全
為了使得類型安全,約束用戶行為磷醋,可以將常量定義為強(qiáng)類型的枚舉猫牡。
public enum Operation {
PLUS, MINUS, TIMES, DIVIDE, MOD;
}
重構(gòu)是一個(gè)高度危險(xiǎn)的實(shí)踐活動(dòng),步子越小越安全邓线。
Operations
的apply, operator
方法類型約束得到了改善淌友,但實(shí)現(xiàn)依然充斥壞味道。
public final class Operations {
public static int apply(Operation op, int x, int y) {
switch (op) {
case PLUS: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
case DIVIDE: return x / y;
case MOD: return x % y;
}
throw new AssertionError("Unknown op: " + op);
}
public static String operator(Operation op) {
switch (op) {
case PLUS: return "+";
case MINUS: return "-";
case TIMES: return "*";
case DIVIDE: return "/";
case MOD: return "%";
}
throw new AssertionError("Unknown op: " + op);
}
private Operations() {
}
}
迭代3:引入多態(tài)
將Operation
重構(gòu)為具有多態(tài)能力枚舉類骇陈,遵循了OCP
良好的設(shè)計(jì)原則震庭,使得程序更加具有彈性,但存在較多的「語法噪聲」你雌。
public enum Operation {
PLUS("+") {
@Override
public int apply(int x, int y) {
return x + y;
}
},
MINUS("-") {
@Override
public int apply(int x, int y) {
return x - y;
}
},
TIMES("*") {
@Override
public int apply(int x, int y) {
return x * y;
}
},
DIVIDE("/") {
@Override
public int apply(int x, int y) {
return x / y;
}
};
MOD("%") {
@Override
public int apply(int x, int y) {
return x % y;
}
};
public abstract int apply(int x, int y);
public String operator() {
return op;
}
private Operation(String op) {
this.op = op;
}
private String op;
}
迭代4:消除噪聲
為了進(jìn)一步消除語法噪聲器联,可以使用Java8 Lambda
進(jìn)一步改善結(jié)構(gòu),使得成設(shè)計(jì)更加直觀明了婿崭。
import java.util.function.BinaryOperator;
public enum Operation {
PLUS("+", (x, y) -> x + y),
MINUS("-", (x, y) -> x - y),
TIMES("*", (x, y) -> x * y),
DIVIDE("/", (x, y) -> x / y),
MOD("%", (x, y) -> x % y);
public int apply(int x, int y) {
return f.apply(x, y);
}
public String operator() {
return operator;
}
private Operation(String operator, BinaryOperator<Integer> f) {
this.operator = operator;
this.f = f;
}
private BinaryOperator<Integer> f;
private String operator;
}
迭代5:使用Scala
使用Scala
拨拓,可進(jìn)一步將語法噪聲消除,并讓用戶接口更加人性化氓栈,程序更加簡單渣磷,漂亮。
sealed trait Operation(val operator: String,
op: (Int, Int) => Int) extends ((Int, Int) => Int) {
override def apply(x: Int, y: Int) = op(x, y)
}
case object plus extends Operation("+", _ + _)
case object minus extends Operation("-", _ - _)
case object times extends Operation("*", _ * _)
case object divide extends Operation("/", _ / _)
case object mod extends Operation("%", _ % _)
約束表達(dá)設(shè)計(jì)
sealed
明確地聲明授瘦,外部不能再對Operation
進(jìn)行擴(kuò)展了醋界,不僅僅使得「模式匹配」成為可能竟宋,更重要的是傳到了作者設(shè)計(jì)的意圖,并在編譯時(shí)約束用戶的行為形纺。
同樣地丘侠,final
修飾case object
,也是出于同樣的目的挡篓。
混血兒
(Int, Int) => Int)
等價(jià)于Function2[Int, Int, Int]
婉陷,而Operation
重寫了Function2.apply
方法,當(dāng)用戶使用plus, minus, times, divide
時(shí)官研,其表現(xiàn)得更像函數(shù)的行為秽澳。
scala > plus(1, 1)
res0: Int = 2
當(dāng)調(diào)用operator
方法時(shí),又散發(fā)出OO
的氣息戏羽,Scala
就是一個(gè)與眾不同的「混血兒」担神。
scala > plus.operator
res1: String = +