http://www.cnblogs.com/draem0507/p/4942939.html#1.集合的封裝#undefined 出處
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");
}
}

