基本类型偏执(Primitive Obsession)程序员
- 使用基本类型而不是小对象来实现简单任务(例如货币、范围、电话号码字符串等)。
- 使用常量编码信息(例如一个用于引用管理员权限的常量
USER_ADMIN_ROLE = 1
)。- 使用字符串常量做为字段名在数组中使用。
相似其余大部分坏味道,基本类型偏执诞生于类初建的时候。一开始,可能只是很少的字段,随着表示的特性愈来愈多,基本数据类型字段也愈来愈多。算法
基本类型经常被用于表示模型的类型。你有一组数字或字符串用来表示某个实体。数据库
还有一个场景:在模拟场景,大量的字符串常量被用于数组的索引。编程
大多数编程语言都支持基本数据类型和结构类型(类、结构体等)。结构类型容许程序员将基本数据类型组织起来,以表明某一事物的模型。设计模式
基本数据类型能够当作是机构类型的积木块。当基本数据类型数量成规模后,将它们有组织地结合起来,能够更方便的管理这些数据。数组
以类取代类型码(Replace Type Code with Class)
。引入参数对象(Introduce Parameter Object)
或 保持对象完整(Preserve Whole Object)
。以类取代类型码(Replace Type Code with Class)
将它替换掉。若是你有与类型码相关的条件表达式,可运用 以子类取代类型码(Replace Type Code with Subclass)
或 以状态/策略模式取代类型码(Replace Type Code with State/Strategy)
加以处理。以对象取代数组(Replace Array with Object)
。问题bash
类之中有一个数值类型码,但它并不影响类的行为。session
解决编程语言
以一个新的类替换该数值类型码。函数
问题
某些参数老是很天然地同时出现。
解决
以一个对象来取代这些参数。
问题
你从某个对象中取出若干值,将它们做为某一次函数调用时的参数。
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);复制代码
解决
改成传递整个对象。
boolean withinPlan = plan.withinRange(daysTempRange);复制代码
问题
你有一个不可变的类型码,它会影响类的行为。
解决
以子类取代这个类型码。
问题
你有一个类型码,它会影响类的行为,但你没法经过继承消除它。
解决
以状态对象取代类型码。
问题
你有一个数组,其中的元素各自表明不一样的东西。
String[] row = new String[3];
row[0] = "Liverpool";
row[1] = "15";复制代码
解决
以对象替换数组。对于数组中的每一个元素,以一个字段来表示。
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");复制代码
数据泥团(Data Clumps)
有时,代码的不一样部分包含相同的变量组(例如用于链接到数据库的参数)。这些绑在一块儿出现的数据应该拥有本身的对象。
一般,数据泥团的出现时由于糟糕的编程结构或“复制-粘贴式编程”。
有一个判断是不是数据泥团的好办法:删掉众多数据中的一项。这么作,其余数据有没有于是失去意义?若是它们再也不有意义,这就是个明确的信号:你应该为它们产生一个新的对象。
提炼类(Extract Class)
将它们提炼到一个独立对象中。引入参数对象(Introduce Parameter Object)
将它们组织成一个类。保持对象完整(Preserve Whole Object)
将整个数据对象传入到函数中。问题
某个类作了不止一件事。
解决
创建一个新类,将相关的字段和函数从旧类搬移到新类。
问题
某些参数老是很天然地同时出现。
解决
以一个对象来取代这些参数。
问题
你从某个对象中取出若干值,将它们做为某一次函数调用时的参数。
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);复制代码
解决
改成传递整个对象。
boolean withinPlan = plan.withinRange(daysTempRange);复制代码
过大的类(Large Class)
一个类含有过多字段、函数、代码行。
类一般一开始很小,可是随着程序的增加而逐渐膨胀。
相似于过长函数,程序员一般以为在一个现存类中添加新特性比建立一个新的类要容易。
设计模式中有一条重要原则:职责单一原则。一个类应该只赋予它一个职责。若是它所承担的职责太多,就该考虑为它减减负。
提炼类(Extract Class)
。提炼子类(Extract Subclass)
。提炼接口(Extract Interface)
。复制被监视数据(Duplicate Observed Data)
能够告诉你怎么作。问题
某个类作了不止一件事。
解决
创建一个新类,将相关的字段和函数从旧类搬移到新类。
问题
一个类中有些特性仅用于特定场景。
解决
建立一个子类,并将用于特殊场景的特性置入其中。
问题
多个客户端使用一个类部分相同的函数。另外一个场景是两个类中的部分函数相同。
解决
移动相同的部分函数到接口中。
问题
若是存储在类中的数据是负责 GUI 的。
解决
一个比较好的方法是将负责 GUI 的数据放入一个独立的类,以确保 GUI 数据与域类之间的链接和同步。
过长函数(Long Method)
一个函数含有太多行代码。通常来讲,任何函数超过 10 行时,你就能够考虑是否是过长了。 函数中的代码行数原则上不要超过 100 行。
一般状况下,建立一个新函数的难度要大于添加功能到一个已存在的函数。大部分人都以为:“我就添加这么两行代码,为此新建一个函数实在是小题大作了。”因而,张三加两行,李四加两行,王五加两行。。。函很多天益庞大,最终烂的像一锅浆糊,再也没人能彻底看懂了。因而你们就更不敢轻易动这个函数了,只能恶性循环的往其中添加代码。因此,若是你看到一个超过 200 行的函数,一般都是多个程序员东拼西凑出来的。
一个很好的技巧是:寻找注释。添加注释,通常有这么几个缘由:代码逻辑较为晦涩或复杂;这段代码功能相对独立;特殊处理。 若是代码前方有一行注释,就是在提醒你:能够将这段代码替换成一个函数,并且能够在注释的基础上给这个函数命名。若是函数有一个描述恰当的名字,就不须要去看内部代码到底是如何实现的。就算只有一行代码,若是它须要以注释来讲明,那也值得将它提炼到独立函数中。
提炼函数(Extract Method)
。以查询取代临时变量(Replace Temp with Query)
,引入参数对象(Introduce Parameter Object)
或 保持对象完整(Preserve Whole Object)
。以函数对象取代函数(Replace Method with Method Object)
尝试移动整个函数到一个独立的对象中。分解条件表达式(Decompose Conditional)
。至于循环,应该使用 提炼函数(Extract Method)
将循环和其内的代码提炼到独立函数中。是否像许多人说的那样,增长函数的数量会影响性能?在几乎绝大多数状况下,这种影响是能够忽略不计,因此不用担忧。 此外,如今有了清晰和易读的代码,在须要的时候,你将更容易找到真正有效的函数来重组代码和提升性能。
问题
你有一段代码能够组织在一块儿。
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}复制代码
解决
移动这段代码到一个新的函数中,使用函数的调用来替代老代码。
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}复制代码
问题
将表达式的结果放在局部变量中,而后在代码中使用。
double calculateTotal() {
double basePrice = quantity * itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
else {
return basePrice * 0.98;
}
}复制代码
解决
将整个表达式移动到一个独立的函数中并返回结果。使用查询函数来替代使用变量。若是须要,能够在其余函数中合并新函数。
double calculateTotal() {
double basePrice = quantity * itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
else {
return basePrice * 0.98;
}
}复制代码
问题
某些参数老是很天然地同时出现。
解决
以一个对象来取代这些参数。
问题
你从某个对象中取出若干值,将它们做为某一次函数调用时的参数。
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);复制代码
解决
改成传递整个对象。
boolean withinPlan = plan.withinRange(daysTempRange);复制代码
问题
你有一个过长函数,它的局部变量交织在一块儿,以至于你没法应用提炼函数(Extract Method) 。
class Order {
//...
public double price() {
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// long computation.
//...
}
}复制代码
解决
将函数移到一个独立的类中,使得局部变量成了这个类的字段。而后,你能够将函数分割成这个类中的多个函数。
class Order {
//...
public double price() {
return new PriceCalculator(this).compute();
}
}
class PriceCalculator {
private double primaryBasePrice;
private double secondaryBasePrice;
private double tertiaryBasePrice;
public PriceCalculator(Order order) {
// copy relevant information from order object.
//...
}
public double compute() {
// long computation.
//...
}
}复制代码
问题
你有复杂的条件表达式。
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * winterRate + winterServiceCharge;
}
else {
charge = quantity * summerRate;
}复制代码
解决
根据条件分支将整个条件表达式分解成几个函数。
if (notSummer(date)) {
charge = winterCharge(quantity);
}
else {
charge = summerCharge(quantity);
}复制代码
过长参数列(Long Parameter List)
一个函数有超过 三、4 个入参。
过长参数列多是将多个算法并到一个函数中时发生的。函数中的入参能够用来控制最终选用哪一个算法去执行。
过长参数列也多是解耦类之间依赖关系时的副产品。例如,用于建立函数中所需的特定对象的代码已从函数移动到调用函数的代码处,但建立的对象是做为参数传递到函数中。所以,原始类再也不知道对象之间的关系,而且依赖性也已经减小。可是若是建立的这些对象,每个都将须要它本身的参数,这意味着过长参数列。
太长的参数列难以理解,太多参数会形成先后不一致、不易使用,并且一旦须要更多数据,就不得不修改它。
以函数取代参数(Replace Parameter with Methods)
。在这里,,“已有的对象”多是函数所属类里的一个字段,也多是另外一个参数。保持对象完整(Preserve Whole Object)
未来自同一对象的一堆数据收集起来,并以该对象替换它们。引入参数对象(Introduce Parameter Object)
为它们制造出一个“参数对象”。问题
对象调用某个函数,并将所得结果做为参数,传递给另外一个函数。而接受该参数的函数自己也可以调用前一个函数。
int basePrice = quantity * itemPrice;
double seasonDiscount = this.getSeasonalDiscount();
double fees = this.getFees();
double finalPrice = discountedPrice(basePrice, seasonDiscount, fees);复制代码
解决
让参数接受者去除该项参数,并直接调用前一个函数。
int basePrice = quantity * itemPrice;
double finalPrice = discountedPrice(basePrice);复制代码
问题
你从某个对象中取出若干值,将它们做为某一次函数调用时的参数。
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);复制代码
解决
改成传递整个对象。
boolean withinPlan = plan.withinRange(daysTempRange);复制代码
问题
某些参数老是很天然地同时出现。
解决
以一个对象来取代这些参数。