31天重构学习笔记(java版本)
准备下周分享会的内容,无意间看到.net版本的重构31天,花了两个小时看了下,可以看成是Martin Fowler《重构》的精简版
原文地址:http://www.lostechies.com/blogs/sean_chambers/archive/2009/07/31/31-days-of-refactoring.aspx
原文代码地址:https://github.com/schambers/days-of-refactoring
圣殿骑士写的.net版本的读书笔记地址:http://www.cnblogs.com/KnightsWarrior/p/31DaysOfRefactoring.html
有百度文库的可以直接下载:http://wenku.baidu.com/view/27423a5c3b3567ec102d8a0c.html?pn=51
抽个时间(就这周六吧,不过得先把分享会的PPT写好 囧),把java版本的写下,先mark下
周六总算是写完了,不过还想是多跨了一周 囧。有时间,在加下描点,还有pdf下载吧。累,回家咯 ^^ --20151114
1.集合的封装
/**
* @title 封装集合对象,不要暴露太多方法给外部访问内部数据
* @desc
* @atuh lwx
* @createtime on 2015/11/12 23:50
*/
public class Day_1 { public static void main(String[] args) { Day1Test day1Test = new Day1Test(); //获取到了内部对象
List<String> list = day1Test.getList(); //肆无忌惮的操作
list.add("a"); day1Test.iterator(); //正确的做法
Day1Test2 day1Test2 = new Day1Test2(); //获取到了内部对象
List<String> list2 = day1Test2.getList(); //肆无忌惮的操作
list2.add("a"); day1Test2.iterator(); } static class Day1Test { private List<String> list = new ArrayList<String>(); public List getList() { return list; } //模拟不暴露给外部
protected void add(String value) {
list.add(value); } protected void remove(String value) { list.remove(value);
} public void iterator() { for (String str : list) {
System.out.println(str);
} } } static class Day1Test2 { private List<String> list = new ArrayList<String>(); public List getList() { return new ArrayList(list); } //模拟不暴露给外部
protected void add(String value) {
list.add(value); } protected void remove(String value) { list.remove(value);
} public void iterator() { for (String str : list) {
System.out.println(str);
} } } }
2.移动方法
Move method does exactly what it sounds like, move a method to a better location(移动方法到更合适的位置)
public class Day_2 { public static void main(String[] args) { } } class BankAccount1
{
public BankAccount1(int accountAge, int creditScore, AccountInterest1 accountInterest)
{
AccountAge = accountAge;
CreditScore = creditScore;
AccountInterest1 = accountInterest;
} public int AccountAge ;
public int CreditScore;
public AccountInterest1 AccountInterest1 ;
} class AccountInterest1
{
public BankAccount1 Account ; public AccountInterest1(BankAccount1 account)
{
Account = account;
} public double InterestRate()
{
return CalculateInterestRate();
} public boolean IntroductoryRate()
{
return CalculateInterestRate() < 0.05;
} public double CalculateInterestRate()
{
if (Account.CreditScore > 800)
return 0.02; if (Account.AccountAge > 10)
return 0.03; return 0.05;
}
} class BankAccount {
public BankAccount(int accountAge, int creditScore, AccountInterest accountInterest) {
AccountAge = accountAge;
CreditScore = creditScore;
AccountInterest = accountInterest;
} public int AccountAge;
public int CreditScore;
public AccountInterest AccountInterest; //这个方法跟BankAccount没有直接关系
public double CalculateInterestRate() {
if (CreditScore > 800)
return 0.02; if (AccountAge > 10)
return 0.03; return 0.05;
}
} class AccountInterest {
public BankAccount Account; public AccountInterest(BankAccount account) {
Account = account;
} public double InterestRate() {
return Account.CalculateInterestRate();
} public boolean IntroductoryRate() {
{
return Account.CalculateInterestRate() < 0.05;
}
}
}
3.提升方法
简单点说,如果子类都有相同的方法,那就应该将方法提上到父类层
abstract class Vehicle {
// other methods
} class Car extends Vehicle {
public void Turn(String str) {
// code here
}
} public class Motorcycle extends Vehicle {
public void Turn(String str) {
// code here
}
}
提升后的结构
abstract class Vehicle1 {
public void Turn(String str) {
// code here
}
} class Car1 extends Vehicle1 { } public class Motorcycle1 extends Vehicle1 { }
4.下移方法
与第三个上升方法相比,有时候,父类的方法,随着业务的变化,只适合部分子类的时候,则需要将父类的方法下移到具体需要的子类中,这样才符合接口最小原则^^
abstract class Animal {
//狗吠
public void Bark() {
// code to bark
}
} class Dog extends Animal {
} class Cat extends Animal {
}
正常小猫是不会狗吠的,当然,有些接口可能当初定义的时候,有些子类还未出现,因此不会有这样的问题。随着业务的增加,这样的问题出现了,那么,我们就要及时的将接口下移
abstract class Animal1 { } class Dog1 extends Animal1 { //狗吠
public void Bark() {
// code to bark
}
} class Cat1 extends Animal1 {
}
5.提升字段
同提升方法,思路一样的,就不多说了
abstract class Account {
} public class CheckingAccount extends Account {
private int _minimumCheckingBalance = 5;
} public class SavingsAccount extends Account {
private int _minimumSavingsBalance = 5;
}
上升后的结构
abstract class Account1 {
protected int _minimumCheckingBalance = 5;
} public class CheckingAccount1 extends Account1 { } public class SavingsAccount1 extends Account1 {
}
6.下移字段
abstract class Task {
protected String _resolution;
} public class BugTask extends Task {
} public class FeatureTask extends Task {
}
改造后的情况
abstract class Task1 { } class BugTask1 extends Task1 { protected String _resolution;
} class FeatureTask1 extends Task1 {
}
7.重命名(类、方法、参数)
demo就不上,只提一点,命名规则不要担心太长,而选择简写,这样反而为后期的维护带来麻烦。
8.使用委托代替继承
设计模式中,很多模式就使用了委托的方式,来解耦继承带来的强依赖,比如装饰者,适配器模式等等。
class Sanitation {
public String WashHands() {
return "Cleaned!";
}
} public class Child extends Sanitation {
}
正确的做法
class Sanitation1 {
public String WashHands() {
return "Cleaned!";
}
} class Child1 {
private Sanitation1 Sanitation; public Child1() {
Sanitation = new Sanitation1();
} public String WashHands() {
return Sanitation.WashHands();
}
}
上述其实就是代理者模式的框架思路了,如果把Sanitation1暴露出来,就是装饰者了。
9.提取接口
官方已经找不到这个页面的链接了,参考了其他地方,做法其实也很简单,就是遵循了接口最小原则来设计的
interface Bird { public void eat(); public void fly(); //我们假设有的鸟是不会唱歌的
public void song(); }
重新设计后
interface Bird1 { public void eat(); public void fly(); } interface SongBird extends Bird1 { //我们假设有的鸟是不会唱歌的
public void song();
}
10.提取方法
提取方法是重构中很常见到的一种手法。他可以通过方法名,增加代码的可读性,减少不必要的注释说明。
class Receipt {
private List<Float> discounts;
private List<Float> itemTotals; public float CalculateGrandTotal() {
float subTotal = 0f;
for (Float itemTotal : itemTotals)
subTotal += itemTotal; if (discounts.size() > 0) {
for (Float discount : discounts)
subTotal -= discount;
} float tax = subTotal * 0.065f; subTotal += tax; return subTotal;
}
}
使用分离方法后的结构
class Receipt1 {
private List<Float> discounts;
private List<Float> itemTotals; public float CalculateGrandTotal() {
float subTotal = 0f;
subTotal=addItemTotals(itemTotals);
subTotal=minuteDiscounts(itemTotals);
subTotal=calcTax(subTotal);
return subTotal;
} float addItemTotals(List<Float> itemTotals){ float subTotal = 0f;
for (Float itemTotal : itemTotals) {
subTotal += itemTotal;
}
return subTotal;
} float minuteDiscounts(List<Float> discounts){
float subTotal = 0f;
if (discounts.size() > 0) {
for (Float discount : discounts)
subTotal -= discount;
}
return subTotal;
}
float calcTax( float subTotal){
float tax = subTotal * 0.065f;
subTotal += tax;
return subTotal;
} }
11.切换到策略模式
很多时候,要完成目标的方式不是只有一种,当我们需要使用不同的条件,来获取不同的结果的时候,我们可以使用策略模式,这样,不会因为新增加一个条件,而去修改判断逻辑
public class ClientCode {
public int CalculateShipping() {
ShippingInfo shippingInfo = new ShippingInfo();
return shippingInfo.CalculateShippingAmount(State.Alaska);
}
} public enum State {
Alaska,
NewYork,
Florida; } public class ShippingInfo {
public int CalculateShippingAmount(State shipToState) { if (shipToState == State.Alaska) { return GetAlaskaShippingAmount();
} else if (shipToState == State.NewYork) { return GetNewYorkShippingAmount();
} else if (shipToState == State.Florida) { return GetFloridaShippingAmount();
} else
return 0;
}
} private int GetAlaskaShippingAmount() {
return 15;
} private int GetNewYorkShippingAmount() {
return 10;
} private int GetFloridaShippingAmount() {
return 3;
}
如果判断条件足够简单,上述做法,其实是可以容忍的,但是,如果Getxx方法变的足够复杂的时候,考虑到单一责任原则,一个类的变化,有且只有一个原因引起,这样,每个判断条件方法发生变化,类都必须做出修改,
这样就不合适了。而且使用类封装,可以更好的实现复用。
static class ShippingInfo1{ //模拟一个工厂
private static Map<State,CalculateShippingAmountStrategy>strategyFactory=new HashMap<State, CalculateShippingAmountStrategy>(); static {
strategyFactory.put(State.Alaska,new GetAlaskaShippingAmount());
strategyFactory.put(State.NewYork,new GetNewYorkShippingAmount());
strategyFactory.put(State.Florida,new GetFloridaShippingAmount()); } public int CalculateShippingAmount(State shipToState) { return strategyFactory.get(shipToState).calc(); } } interface CalculateShippingAmountStrategy{ public int calc();
} static class GetAlaskaShippingAmount implements CalculateShippingAmountStrategy{ public int calc(){ return 15;
} }
static class GetNewYorkShippingAmount implements CalculateShippingAmountStrategy{ public int calc(){ return 10;
} }
static class GetFloridaShippingAmount implements CalculateShippingAmountStrategy{ public int calc(){ return 3;
} }
12.解耦依赖
六大设计原则中的最少知识原则(迪米特)说的就是,对依赖的了解,降低到最少。作者强调,当我们进行单元测试的时候,我们就需要一定的隔离,否则无法进行mock.这个自己也是深有体会。
良好的隔离,确实可以让单元测试的Mock变得非常的简单和容易。先看下面的例子,由于AnimalFeedingService直接依赖了静态类Feeder,因此当我们需要只测试FoodBowlEmpty的逻辑判断走向的时候,必然会触发
Feeder的方法,这其实并不是我们想要的。但是又无法直接对静态类进行mock.
public class AnimalFeedingService
{
private boolean FoodBowlEmpty; public void Feed()
{
if (FoodBowlEmpty)
Feeder.ReplenishFood(); // more code to feed the animal
}
} public static class Feeder
{
public static void ReplenishFood()
{
// fill up bowl
}
}
解决的办法,就是让Service跟静态的对象解耦
public class AnimalFeedingService1
{
public IFeederService FeederService ; public AnimalFeedingService1(IFeederService feederService)
{
FeederService = feederService;
} private boolean FoodBowlEmpty ; public void Feed()
{
if (FoodBowlEmpty)
FeederService.ReplenishFood(); // more code to feed the animal
}
} public interface IFeederService
{
void ReplenishFood();
} public class FeederService implements IFeederService
{
public void ReplenishFood()
{
Feeder.ReplenishFood();
}
}
13.提取方法对象
这并不是一种很常见的重构手段,即当我们对象中定义了很多变量,及其需要利用这些变量进行一些业务操作的时候,可以考虑将方法提取到一个新的类中,这样就解耦了变量与逻辑操作的直接关联。
也比较符合单一责任原则。
public class OrderLineItem
{
public int Price ;
} public class Order
{
private List<OrderLineItem> OrderLineItems ;
private List<Integer> Discounts;
private int Tax ; public int Calculate()
{
int subTotal = 0; // Total up line items
for (OrderLineItem lineItem : OrderLineItems)
{
subTotal += lineItem.Price;
} // Subtract Discounts
for (int discount : Discounts)
subTotal -= discount; // Calculate Tax
int tax = subTotal * Tax; // Calculate GrandTotal
int grandTotal = subTotal + tax; return grandTotal;
}
}
咋看,代码并没有什么大的问题,order中定义了很多关于自身的属性,还有对属性的一些业务操作,但是,计算价格,其实并不是order对象本身应该关系的。因此,需要引入一个计算order price能力的类
public class Order1
{
private List<OrderLineItem> OrderLineItems ;
private List<Integer> Discounts;
private int Tax ; public int Calculate(){ return new OrderCalculator(this).Calculate();
} } public class OrderCalculator{
private Order1 order;
private List<OrderLineItem> OrderLineItems ;
private List<Integer> Discounts;
private int Tax ; public OrderCalculator(Order1 order){ this.order=order;
} public int Calculate()
{
int subTotal = 0; // Total up line items
for (OrderLineItem lineItem : OrderLineItems)
{
subTotal += lineItem.Price;
} // Subtract Discounts
for (int discount : Discounts)
subTotal -= discount; // Calculate Tax
int tax = subTotal * Tax; // Calculate GrandTotal
int grandTotal = subTotal + tax; return grandTotal;
} }
14.单一责任
上面的问题,其实一直提到设计原则,自然也提到了单一责任原则SRP,要学重构,SRP是必然要知道,且学会的思想,并且灵活应用到重构代码中。
下面作者举了一个Video的例子,Video类中有两个方法,分别负责统计客户购买的Video数量,并且计算每个客户的购买金额
public class Video
{
public void PayFee(int fee)
{
} public void RentVideo(Video video, Customer customer)
{
customer.Videos.add(video);
} public int CalculateBalance(Customer customer)
{
return customer.LateFees.size();
}
} public class Customer
{
public List<Integer> LateFees;
public List<Video> Videos ;
}
很明显,顾客购买Video的金额,并不是Video本身应该关系的,而是每个Customer应该关系的,因此,需要将计算购买金额的方法下移到Customer类中来完成
public class Video1
{
public void RentVideo(Video1 video, Customer1 customer)
{
customer.Videos.add(video);
}
} public class Customer1
{
public List<Integer> LateFees;
public List<Video1> Videos ; public void PayFee(int fee)
{
} public int CalculateBalance(Customer1 customer)
{
return customer.LateFees.size();
}
}
15.移除拷贝
当我们有两段一样的代码的时候,很明显,我们需要对他进行简单的封装(具体如何处理,这里先不说,技巧很多种),让重复的代码彻底消息掉。这个可能也是重构最简单,也是最好用的一种方式了
public class MedicalRecord
{
public Date DateArchived ;
public boolean Archived; public void ArchiveRecord()
{
Archived = true;
DateArchived = new Date();
} public void CloseRecord()
{
Archived = true;
DateArchived = new Date();
}
}
我们模拟了一段在两个方法中都存在相同逻辑的代码,这时候,我们就要对他进行重构了
public class MedicalRecord1
{
public Date DateArchived ;
public boolean Archived; public void ArchiveRecord()
{
init();
} public void CloseRecord()
{
init();
}
public void init()
{
Archived = true;
DateArchived = new Date();
}
}
16.封装条件
简单来说,就是对复杂的条件逻辑判断,进行单独处理,这样,当条件参数发生变化的时候,不会影响到真实的业务逻辑流程
public class RemoteControl {
private String[] Functions;
private String Name;
private int CreatedYear; public String PerformCoolFunction(String buttonPressed) {
// Determine if we are controlling some extra function
// that requires special conditions
if (Functions.length > 1 && Name == "RCA" && CreatedYear > new Date().getYear() - 2) {
return "doSomething"; }
return "";
}
}
如何处理呢
public class RemoteControl2 {
private String[] Functions;
private String Name;
private int CreatedYear; public String PerformCoolFunction(String buttonPressed) {
// Determine if we are controlling some extra function
// that requires special conditions
if (HasExtraFunctions()) {
return "doSomething"; }
return "";
} private boolean HasExtraFunctions()
{
return Functions.length > 1 && Name == "RCA" && CreatedYear > new Date().getYear() - 2 ;
} }
17.提取父类
如何理解呢?简单来说,就是当我们发现定义的方法,可以被抽象成更高层次对象的时候,就需要考虑抽象一个更上层的父类,并将接口迁移到父类中去定义
public class Dog
{
public void EatFood()
{
// eat some food
} public void Groom()
{
// perform grooming
}
}
重构后的效果
public class Animal
{
public void EatFood()
{
// eat some food
} public void Groom()
{
// perform grooming
}
} public class Dog1 extends Animal
{
}
但是需要注意,过多的继承容易引起耦合,所以有时候,我们需要考虑接口或则聚合来解决继承带来的强依赖。
18.条件判断代替异常
这个其实在很多语言规则中,都有提到,就是不能使用异常来代替控制逻辑,比如《effective java》一书中就有提到。
public class Microwave
{ public boolean Start()
{
boolean foodCooked = false;
try
{
//do something perhaps throw new exception
foodCooked = true;
}
catch (Exception e)
{
foodCooked = false;
} return foodCooked;
}
}
}
重构后的效果
public class Microwave1
{ public boolean Start()
{
boolean foodCooked = false;
//mock 模拟先判断是否满足某种条件,避免异常发生
if(true){
//do something
foodCooked = true;
}else { foodCooked = false;
} return foodCooked;
}
}
19.拓展工厂类
将创建对象的过程给封装起来,这就是工厂模式的设计初衷。将一些列有关系的产品簇组合成一个最终的产品,便是抽象工厂了。好像讲偏了,回归正题,使用工厂模式,从重构角度来看,就是为了实现单一职责,使得
代码更加稳定。
public class PoliceCarController
{
public PoliceCar New(int mileage, boolean serviceRequired)
{
PoliceCar policeCar = new PoliceCar();
policeCar.ServiceRequired = serviceRequired;
policeCar.Mileage = mileage; return policeCar;
}
} class PoliceCar{ public boolean ServiceRequired;
public int Mileage;
}
重构后的效果
public interface IPoliceCarFactory
{
PoliceCar Create(int mileage, boolean serviceRequired);
} public class PoliceCarFactory implements IPoliceCarFactory
{
public PoliceCar Create(int mileage, boolean serviceRequired)
{
PoliceCar policeCar = new PoliceCar();
policeCar.ServiceRequired = serviceRequired;
policeCar.Mileage = mileage;
return policeCar;
}
} public class PoliceCarController1
{
public IPoliceCarFactory PoliceCarFactory ; public PoliceCarController1(IPoliceCarFactory policeCarFactory)
{
PoliceCarFactory = policeCarFactory;
} public PoliceCar New(int mileage, boolean serviceRequired)
{
return PoliceCarFactory.Create(mileage, serviceRequired);
}
}
20.提取子类
这个方式,之前好像已经提到的下移方法类似,也是为了遵循接口隔离原则。
public interface Ball
{ public void play(); public void size(); //打气
public void pumpUp(); }
球,可以用来玩,也都有他们的大小,但是不是每种球,都需要打球的pumpUp
因此需要将pumpUp方法下移到具体子类中
public interface BasketBall extends Ball2{ //打气
public void pumpUp();
} public interface Ball2
{ public void play(); public void size(); }
21合并集成
//将子类的方法迁移到父类中 不多说了,我想静静
public abstract class Website
{
public abstract String Title();
} public abstract class StudentWebsite extends Website
{
public abstract boolean IsActive() ;
}
改造后的结构
public abstract class Website2
{
public abstract String Title();
public abstract boolean IsActive() ;
} public abstract class StudentWebsite2 extends Website
{ }
虽然感觉跟上移方法很像,但是确实在职责区分中,一定需要判断好,方法到底归属于父类还是子类。
22.分解方法
是不是想到了"提取方法"了,omg。果然够2,我只贴代码,不说话 orz
public class CashRegister
{
public CashRegister()
{
Tax = 0.06f;
} private float Tax ; public void AcceptPayment(Customer customer, List<Product> products, int payment)
{
float subTotal = 0f;
for (Product product : products)
{
subTotal += product.Price;
} for (Product product : products)
{
subTotal -= product.AvailableDiscounts;
} float grandTotal = subTotal * Tax; customer.DeductFromAccountBalance(grandTotal);
}
} public class Customer
{
public void DeductFromAccountBalance(float amount)
{
// deduct from balance
}
} public class Product
{
public int Price ;
public int AvailableDiscounts ;
}
方法封装后的结构
public class CashRegister2
{
public CashRegister2()
{
Tax = 0.06f;
} private float Tax ;
private List<Product> Products; public void AcceptPayment(Customer customer, List<Product> products, int payment)
{
int subTotal = CalculateSubtotal(); subTotal = SubtractDiscounts(subTotal); float grandTotal = AddTax(subTotal); SubtractFromCustomerBalance(customer, grandTotal);
} private void SubtractFromCustomerBalance(Customer customer, float grandTotal)
{
customer.DeductFromAccountBalance(grandTotal);
} private float AddTax(int subTotal)
{
return subTotal * Tax;
} private int SubtractDiscounts(int subTotal)
{
for (Product product : Products)
{
subTotal -= product.AvailableDiscounts;
}
return subTotal;
} private int CalculateSubtotal()
{
int subTotal = 0;
for (Product product : Products)
{
subTotal += product.Price;
}
return subTotal;
}
}
23.引入参数对象
此重构模式非常的好用,也非常容易上手,重点推荐,下面代码中,可以比较下
public void test(boolean check, String str, int order) { //todo
} public void test(Argument argument) { //todo
} class Argument { boolean check;
String str;
int order; }
24.分解复杂判断
原意是移除箭头模式,简言之,即对于复杂的逻辑判断if else{if else ..}类似这样嵌套判断,可以有一些重构的技巧
public class Security
{
public List list; public Security(List list)
{
this.list = list;
} public boolean HasAccess(Date date, String []arrs, List<String> exemptions)
{
boolean hasPermission = false; if (date != null)
{
if (arrs != null)
{
if (arrs.length == 0)
{
if (null!=exemptions&&exemptions.get(0).equals("abc"))
{
hasPermission = true;
}
}
}
} return hasPermission;
}
}
如何重构呢,比较通用的一个做法是判断一次,return一次
public boolean HasAccess2(Date date, String[] arrs, List<String> exemptions) {
boolean hasPermission = false; if (date == null||arrs==null) {
return false;
}
if(arrs.length!=0){
return false;
}
if (null != exemptions && exemptions.get(0).equals("abc")) {
return true;
} return false;
}
最后是stackoverflow上,关于arrowhead pattern的一些建议:http://stackoverflow.com/questions/17804005/how-to-prevent-the-arrowhead-anti-pattern/17813388
25.引入契约检查
Design by contract,即要求我们对输入和输出都进行验证,已保证系统不会因为意想不到的情况出现,而导致程序出现不可以控的情况
先看下面的例子
public class CashRegister
{
public int TotalOrder(List<String> products, Calendar calendar)
{
int orderTotal =products.size(); orderTotal+=calendar.get(Calendar.SUNDAY); return orderTotal;
}
}
采用DBC后的重构效果
public int TotalOrder2(List<String> products, Calendar calendar) { if (products == null) { throw new NullPointerException("products must not be empty");
}
if (products.size() == 0) { throw new ArithmeticException("products's size must more than one");
}
//calendar校验省略 int orderTotal = products.size(); orderTotal += calendar.get(Calendar.SUNDAY);
//输出校验
if (orderTotal == 0) { throw new SecurityException("orderTotal's value must bigger than 0");
} return orderTotal;
}
更多关于DBC:https://en.wikipedia.org/wiki/Design_by_contract
26.避免双重否定
没什么好说的,直接上代码吧。
/**
* @title 避免双重否定
* @desc
* @atuh lwx
* @createtime on 2015/11/14 16:27
*/
public class Day_26 { static boolean isEmpty(String str){ if(null==str||str.length()==0){ return true;
}
return false; } static boolean isNotEmpty(String str){ return !isEmpty(str); } public static void main(String[] args) { if(!isEmpty("")){ //todo
}
//
if(isNotEmpty("")){ } } }
27.移除上帝类
如何理解所谓的上帝类呢,说白了,就是一些“功能强大的工具/管理类”,他可能庞大到整个业务系统只会有一个的工具类,这样就违反了单一责任原则。
public class CustomerService {
public int CalculateOrderDiscount(String str) {
// do work
return 0;
} public boolean CustomerIsValid(String str) {
// do work
return true;
} public List<String> GatherOrderErrors() {
// do work
return null;
} public void Register(Object customer) {
// do work
} public void ForgotPassword(Object customer) {
// do work
}
}
职责明确后的结构
public class CustomerService2 {
public int CalculateOrderDiscount(String str) {
// do work
return 0;
} public boolean CustomerIsValid(String str) {
// do work
return true;
} public List<String> GatherOrderErrors() {
// do work
return null;
} } public class CustomerRegistrationService{ public void Register(Object customer) {
// do work
} public void ForgotPassword(Object customer) {
// do work
}
}
28.重命名布尔类型方法
如果有Boolean类型参数,则为了简化外部调用带来的困难,一般会使用重命名方法来简化调用带来的困难,当然,也可以通过重载来弱化boolean变量在使用中带来的不变
public class BankAccount
{
public void CreateAccount( Object customer,boolean withChecking, boolean withSavings)
{
// do work
}
}
改造后的结果
public class BankAccount2
{
public void CreateAccountWithChecking(Object customer)
{
CreateAccount(customer, true, false);
} public void CreateAccountWithCheckingAndSavings(Object customer)
{
CreateAccount(customer, true, true);
} private void CreateAccount(Object customer, boolean withChecking, boolean withSavings)
{
// do work
}
}
29.去除中间人
如何理解去除中间人呢?简单理解,就是当A需要通过B去访问C的时候,并且B除了调用C的方法,不在有任何作用的时候,则B就成了所谓的中间人,就应该被delete掉
public class Consumer {
public AccountManager AccountManager; public Consumer(AccountManager accountManager) {
AccountManager = accountManager;
} public void Get(int id) {
Account account = AccountManager.GetAccount(id);
}
} public class AccountManager {
public AccountDataProvider DataProvider; public AccountManager(AccountDataProvider dataProvider) {
DataProvider = dataProvider;
} public Account GetAccount(int id) {
return DataProvider.GetAccount(id);
}
} public class AccountDataProvider {
public Account GetAccount(int id) {
// get account
return null;
}
} class Account { }
重构后的效果
public class Consumer2
{
public AccountDataProvider AccountDataProvider ; public Consumer2(AccountDataProvider dataProvider)
{
AccountDataProvider = dataProvider;
} public void Get(int id)
{
Account account = AccountDataProvider.GetAccount(id);
}
}
这里需要作两点补充:第一,AccountManager当初设计是为了隔离Consumer与AccountProvider,后面可能随着业务形态发生变化,两者可以直接调用的时候,AccountManager对象就失去了意义。
举个简单的例子,我们买电视,都是去超市去买,因为你不可能直接去厂家拿货,如果哪天你的角色变成代理商或则厂家工人了,也许,你就可以内部直接拿货了
第二,有时候,对于两个需要隔离的对象,需要制造一个中间人,来隔离他们。好比,你原先是公司的员工,享受福利,离职后,就不会再有这种福利了。内部的一些东西,你也就接触不到了。
30.尽快返回
return as soon as possible。即对之前的复杂逻辑判断的一个侧面说明了。
public class Order {
public Object Customer; public int CalculateOrder(Object customer, List<Object> products, int discounts) {
Customer = customer;
int orderTotal = 0; if (products.size() > 0) {
orderTotal = products.size();
if (discounts > 0) {
orderTotal -= discounts;
}
} return orderTotal;
}
}
改造后
public class Order2 {
public Object Customer; public int CalculateOrder(Object customer, List<Object> products, int discounts) {
Customer = customer;
int orderTotal = 0; if (products.size() == 0) {
return 0;
} orderTotal = products.size();
if (discounts > 0) {
orderTotal -= discounts;
} return orderTotal;
}
}
31.使用多态代替条件
上面其实也提到了策略模式替换多条件,其实是类似的。如果对java的单双派机制,有更多了解的,可以移步我之前写的一篇文章,java单双派机制理解
/**
* @title 使用多态代替条件判断
* @desc
* @atuh lwx
* @createtime on 2015/11/14 17:41
*/
public class Day_31 { public static void main(String[] args) { Day_31 day_31 = new Day_31(); Parent parent = new Parent();
Son son = new Son();
Daughter daughter = new Daughter(); day_31.invokeSay(parent);
day_31.invokeSay(son);
day_31.invokeSay(daughter); System.out.println("华丽的分割线");
//使用动态方式
day_31.invokeSay2(parent);
day_31.invokeSay2(son);
day_31.invokeSay2(daughter);
//考虑重载解决 -->又涉及到单分派-->通过使用访问者模式来解决 } public void invokeSay(Object parent) { if (parent instanceof Son) { ((Son) parent).say();
} else if (parent instanceof Daughter) { ((Daughter) parent).say();
} else {
((Parent)parent).say();
}
}
public void invokeSay2(Parent parent) { parent.say();
} } class Parent { public void say() { System.out.println("parent say");
} } class Son extends Parent { public void say() { System.out.println("Son say");
}
} class Daughter extends Parent { public void say() { System.out.println("Daughter say");
}
}
31天重构学习笔记(java版本)的更多相关文章
- [java学习笔记]java语言核心----面向对象之this关键字
一.this关键字 体现:当成员变量和函数的局部变量重名时,可以使用this关键字来区别:在构造函数中调用其它构造函数 原理: 代表的是当前对象. this就是所在函数 ...
- [java学习笔记]java语言核心----面向对象之构造函数
1.构造函数概念 特点: 函数名与类名相同 不用定义返回值类型 没有具体的返回值 作用: 给对象进行初始化 注意: 默认构造函数 多个构造函数是以重载出现的 一个类中如果 ...
- 大数据学习笔记——Java篇之网络编程基础
Java网络编程学习笔记 1. 网络编程基础知识 1.1 网络分层图 网络分层分为两种模型:OSI模型以及TCP/IP网络模型,前者模型分为7层,是一个理论的,参考的模型:后者为实际应用的模型,具体对 ...
- Java学习笔记 -- Java定时调度工具Timer类
1 关于 (时间宝贵的小姐姐请跳过) 本教程是基于Java定时任务调度工具详解之Timer篇的学习笔记. 什么是定时任务调度 基于给定的时间点,给定的时间间隔或者给定的执行次数自动执行的任务. 在Ja ...
- 大数据学习笔记——Java篇之IO
IO学习笔记整理 1. File类 1.1 File对象的三种创建方式: File对象是一个抽象的概念,只有被创建出来之后,文件或文件夹才会真正存在 注意:File对象想要创建成功,它的目录必须存在! ...
- 大数据学习笔记——Java篇之集合框架(ArrayList)
Java集合框架学习笔记 1. Java集合框架中各接口或子类的继承以及实现关系图: 2. 数组和集合类的区别整理: 数组: 1. 长度是固定的 2. 既可以存放基本数据类型又可以存放引用数据类型 3 ...
- [Java学习笔记] Java异常机制(也许是全网最独特视角)
Java 异常机制(也许是全网最独特视角) 一.Java中的"异常"指什么 什么是异常 一句话简单理解:异常是程序运行中的一些异常或者错误. (纯字面意思) Error类 和 Ex ...
- 学习笔记——Java类和对象
今天学习了Java的类和对象的相关知识,由于Java面向对象的编程的思想和C++几乎一样,所以需要更多的关注Java的一些不同之处. 1.类 1.1 在类这一块,除了基本的成员变量,成员方法,构造函数 ...
- 设计模式学习笔记——java中常用的设计模式
单例设计模式(Singleton Pattern) 观察者模式(Observer Pattern) 工厂模式(Factory Pattern) 策略模式(Strategy Pattern) 适配器模式 ...
随机推荐
- (C++)关于拷贝构造函数 Copy Constructor
题目: In which of the following scenarios is a Copy Constructor called or invoked? A. When no conve ...
- Skyline开发2-第一个程序
来试试Skyline的Hello World.使用的工具是VS2017+Skyline6.5 加载组件 在工具箱右键新建skyline选项卡,在skyline选项卡上右键选择项,在弹出的"选 ...
- iOS NSNotificationCenter 最基本使用
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:] , @"a ...
- JavaScript 之 动态加载JS代码或JS文件
2.动态加载JS文件 <script type="text/javascript"> function loadScript(url, callback) { var ...
- 002-Go通过ioutil 读写文件
1.读取文件内容 package main import( "io/ioutil" "fmt" ) func main(){ b,err := ioutil.R ...
- LNMP一键安装包-CentOS/Ubuntu/Debian自动安装Nginx,MySQL,PHP
适用环境: 系统支持:CentOS.Ubuntu.Debian 内存要求:≥128M 安装了什么: 1.Nginx-1.2.1 2.MySQL 5.5.25 3.PHP 5.2.17或PHP 5.3. ...
- python object对象
动态语言的对象属性 既然都是动态语言,自然python和熟知的JavaScript很像,建一个空对象用来存放所有的数据,看看js: var data = {}; data.name = 'CooMar ...
- Generating phar.phar chmod: cannot access `ext/phar/phar.phar': No such file or directory make: [ext/phar/phar.phar] Error 1 (ignored)
make install出现了cp: cannot stat `ext/phar/phar.phar': No such file or directory 于是我又: cd ext/phar/ls ...
- 在quartz的Job中获得Spring的WebApplicationContext或ServletContext
有时候我们需要在web工程中定时器类里面获得spring的IOC容器,即WebApplicationContext,用它来获取实现了某接口的所有的bean,因为@Autowired貌似只能注入单个be ...
- 解决input 有多少个radio绑定change事件,手动触发就会执行多少次问题
如题,相信大家都会遇到这个问题,那么为什么会触发多次呢?其实当你用jquery绑定onchange事件的时候你就无形中给每个radio绑定了事件,所以才会出现执行多少次的问题了,那么如何解决呢,其实这 ...