1. 程式人生 > >開發中濫用面向物件,你是否違背了程式設計原則

開發中濫用面向物件,你是否違背了程式設計原則

Switch 宣告

Switch 宣告(Switch Statements)

你有一個複雜的 switch 語句或 if 序列語句。

問題原因

面向物件程式的一個最明顯特徵就是:少用 switch 和 case 語句。從本質上說,switch 語句的問題在於重複(if 序列也同樣如此)。你常會發現 switch 語句散佈於不同地點。如果要為它新增一個新的 case 子句,就必須找到所有 switch語句並修改它們。面向物件中的多型概念可為此帶來優雅的解決辦法。

大多數時候,一看到 switch 語句,就應該考慮以多型來替換它。

解決方法

  • 問題是多型該出現在哪?switch 語句常常根據型別碼進行選擇,你要的是“與該型別碼相關的函式或類”,所以應該運用 提煉函式(Extract Method) 將 switch 語句提煉到一個獨立函式中,再以 搬移函式(Move Method) 將它搬移到需要多型性的那個類裡。
  • 如果你的 switch 是基於型別碼來識別分支,這時可以運用 以子類取代型別碼(Replace Type Code with Subclass)
     或 以狀態/策略模式取代型別碼(Replace Type Code with State/Strategy) 。
  • 一旦完成這樣的繼承結構後,就可以運用 以多型取代條件表示式(Replace Conditional with Polymorphism) 了。
  • 如果條件分支並不多並且它們使用不同引數呼叫相同的函式,多型就沒必要了。在這種情況下,你可以運用 以明確函式取代引數(Replace Parameter with Explicit Methods) 。
  • 如果你的選擇條件之一是 null,可以運用 引入 Null 物件(Introduce Null Object)
     。

收益

  • 提升程式碼組織性。

何時忽略

  • 如果一個 switch 操作只是執行簡單的行為,就沒有重構的必要了。
  • switch 常被工廠設計模式族(工廠方法模式(Factory Method)抽象工廠模式(Abstract Factory))所使用,這種情況下也沒必要重構。

重構方法說明

提煉函式(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);
}

搬移函式(Move Method)

問題

你的程式中,有個函式與其所駐類之外的另一個類進行更多交流:呼叫後者,或被後者呼叫。

解決

在該函式最常引用的類中建立一個有著類似行為的新函式。將舊函式變成一個單純的委託函式,或是舊函式完全移除。

以子類取代型別碼(Replace Type Code with Subclass)

問題

你有一個不可變的型別碼,它會影響類的行為。

解決

以子類取代這個型別碼。

 

以狀態/策略模式取代型別碼(Replace Type Code with State/Strategy)

問題

你有一個型別碼,它會影響類的行為,但你無法通過繼承消除它。

 

解決

以狀態物件取代型別碼。

以多型取代條件表示式(Replace Conditional with Polymorphism)

問題

你手上有個條件表示式,它根據物件型別的不同而選擇不同的行為。

class Bird {
  //...
  double getSpeed() {
    switch (type) {
      case EUROPEAN:
        return getBaseSpeed();
      case AFRICAN:
        return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
      case NORWEGIAN_BLUE:
        return (isNailed) ? 0 : getBaseSpeed(voltage);
    }
    throw new RuntimeException("Should be unreachable");
  }
}

解決

將這個條件表示式的每個分支放進一個子類內的覆寫函式中,然後將原始函式宣告為抽象函式。

abstract class Bird {
  //...
  abstract double getSpeed();
}

class European extends Bird {
  double getSpeed() {
    return getBaseSpeed();
  }
}
class African extends Bird {
  double getSpeed() {
    return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
  }
}
class NorwegianBlue extends Bird {
  double getSpeed() {
    return (isNailed) ? 0 : getBaseSpeed(voltage);
  }
}

// Somewhere in client code
speed = bird.getSpeed();

以明確函式取代引數(Replace Parameter with Explicit Methods)

問題

你有一個函式,其中完全取決於引數值而採取不同的行為。

void setValue(String name, int value) {
  if (name.equals("height")) {
    height = value;
    return;
  }
  if (name.equals("width")) {
    width = value;
    return;
  }
  Assert.shouldNeverReachHere();
}

解決

針對該引數的每一個可能值,建立一個獨立函式。

void setHeight(int arg) {
  height = arg;
}
void setWidth(int arg) {
  width = arg;
}

引入 Null 物件(Introduce Null Object)

問題

你需要再三檢查某物件是否為 null。

if (customer == null) {
  plan = BillingPlan.basic();
}
else {
  plan = customer.getPlan();
}

解決

將 null 值替換為 null 物件。

class NullCustomer extends Customer {
  Plan getPlan() {
    return new NullPlan();
  }
  // Some other NULL functionality.
}

// Replace null values with Null-object.
customer = (order.customer != null) ? order.customer : new NullCustomer();

// Use Null-object as if it's normal subclass.
plan = customer.getPlan();

臨時欄位

臨時欄位(Temporary Field)的值只在特定環境下有意義,離開這個環境,它們就什麼也不是了。

問題原因

有時你會看到這樣的物件:其內某個例項變數僅為某種特定情況而設。這樣的程式碼讓人不易理解,因為你通常認為物件在所有時候都需要它的所有變數。在變數未被使用的情況下猜測當初設定目的,會讓你發瘋。 通常,臨時欄位是在某一演算法需要大量輸入時而建立。因此,為了避免函式有過多引數,程式設計師決定在類中建立這些資料的臨時欄位。這些臨時欄位僅僅在演算法中使用,其他時候卻毫無用處。 這種程式碼不好理解。你期望檢視物件欄位的資料,但是出於某種原因,它們總是為空。

解決方法

  • 可以通過 提煉類(Extract Class) 將臨時欄位和操作它們的所有程式碼提煉到一個單獨的類中。此外,你可以運用 以函式物件取代函式(Replace Method with Method Object) 來實現同樣的目的。
  • 引入 Null 物件(Introduce Null Object) 在“變數不合法”的情況下建立一個 null 物件,從而避免寫出條件表示式。

 

收益

  • 更好的程式碼清晰度和組織性。

 

重構方法說明

提煉類(Extract Class)

問題

某個類做了不止一件事。

解決

建立一個新類,將相關的欄位和函式從舊類搬移到新類。

 

以函式物件取代函式(Replace Method with Method Object)

問題

你有一個過長函式,它的區域性變數交織在一起,以致於你無法應用提煉函式(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.
    //...
  }
}

引入 Null 物件(Introduce Null Object)

問題

你需要再三檢查某物件是否為 null。

if (customer == null) {
  plan = BillingPlan.basic();
}
else {
  plan = customer.getPlan();
}

解決

將 null 值替換為 null 物件。

class NullCustomer extends Customer {
  Plan getPlan() {
    return new NullPlan();
  }
  // Some other NULL functionality.
}

// Replace null values with Null-object.
customer = (order.customer != null) ? order.customer : new NullCustomer();

// Use Null-object as if it's normal subclass.
plan = customer.getPlan();

異曲同工的類

異曲同工的類(Alternative Classes with Different Interfaces)

兩個類中有著不同的函式,卻在做著同一件事。

 

問題原因

這種情況往往是因為:建立這個類的程式設計師並不知道已經有實現這個功能的類存在了。

解決方法

  • 如果兩個函式做同一件事,卻有著不同的簽名,請運用 函式改名(Rename Method) 根據它們的用途重新命名。
  • 運用 搬移函式(Move Method) 、 新增引數(Add Parameter) 和 令函式攜帶引數(Parameterize Method) 來使得方法的名稱和實現一致。
  • 如果兩個類僅有部分功能是重複的,嘗試運用 提煉超類(Extract Superclass) 。這種情況下,已存在的類就成了超類。
  • 當最終選擇並運用某種方法來重構後,也許你就能刪除其中一個類了。

收益

  • 消除了不必要的重複程式碼,為程式碼瘦身了。
  • 程式碼更易讀(不再需要猜測為什麼要有兩個功能相同的類)。

 

何時忽略

  • 有時合併類是不可能的,或者是如此困難以至於沒有意義。例如:兩個功能相似的類存在於不同的 lib 庫中。

重構方法說明

函式改名(Rename Method)

問題

函式的名稱未能恰當的揭示函式的用途。

class Person {
  public String getsnm();
}

解決

修改函式名。

class Person {
  public String getSecondName();
}

搬移函式(Move Method)

問題

你的程式中,有個函式與其所駐類之外的另一個類進行更多交流:呼叫後者,或被後者呼叫。

 

解決

在該函式最常引用的類中建立一個有著類似行為的新函式。將舊函式變成一個單純的委託函式,或是舊函式完全移除。

 

新增引數(Add Parameter)

問題 某個函式需要從呼叫端得到更多資訊。

class Customer {
  public Contact getContact();
}

解決 為此函式新增一個物件函式,讓改物件帶進函式所需資訊。

class Customer {
  public Contact getContact(Date date);
}

令函式攜帶引數(Parameterize Method)

問題

若干函式做了類似的工作,但在函式本體中卻包含了不同的值。


解決

建立單一函式,以引數表達哪些不同的值。

提煉超類(Extract Superclass)

問題

兩個類有相似特性。

解決

為這兩個類建立一個超類,將相同特性移至超類。

被拒絕的饋贈

被拒絕的饋贈(Refused Bequest)

子類僅僅使用父類中的部分方法和屬性。其他來自父類的饋贈成為了累贅。

問題原因

有些人僅僅是想重用超類中的部分程式碼而建立了子類。但實際上超類和子類完全不同。

解決方法

  • 如果繼承沒有意義並且子類和父類之間確實沒有共同點,可以運用 以委託取代繼承(Replace Inheritance with Delegation) 消除繼承。
  • 如果繼承是適當的,則去除子類中不需要的欄位和方法。運用 提煉超類(Extract Superclass) 將所有超類中對於子類有用的欄位和函式提取出來,置入一個新的超類中,然後讓兩個類都繼承自它。

收益

  • 提高程式碼的清晰度和組織性。

重構方法說明

以委託取代繼承(Replace Inheritance with Delegation)

問題

某個子類只使用超類介面中的一部分,或是根本不需要繼承而來的資料。

解決

  1. 在子類中新建一個欄位用以儲存超類;
  2. 調整子類函式,令它改而委託超類;
  3. 然後去掉兩者之間的繼承關係。

 

提煉超類(Extract Superclass)

問題

兩個類有相似特性。

 

解決

為這兩個類建立一個超類,將相同特性移至超類。

相關推薦

開發濫用面向物件是否違背程式設計原則

Switch 宣告 Switch 宣告(Switch Statements) 你有一個複雜的 switch

【Lua學習筆記】 Lua實現面向物件轉自雲風的部落格

local _class={} function class(super) local class_type={} class_type.ctor=false class_type.super=super class_type.new=function(...) local obj={}

輕鬆理解JS面向物件順便搞懂prototype和__proto__

這篇文章主要講一下JS中面向物件以及 __proto__,ptototype和construcator,這幾個概念都是相關的,所以一起講了。 在講這個之前我們先來說說類,瞭解面向物件的朋友應該都知道,如果我要定義一個通用的型別我可以使用類(class)。比如在java中我們可以這樣定義一個類: public

【模型解讀】GoogLeNet的inception結構看懂

文章首發於微信公眾號《與有三學AI》 03 這是深度學習模型解讀第3篇,本篇我們將介紹GoogLeNet v1到v3。 01 Inception V1【1】 GoogLeNet首次出現在2014年ILSVRC 比賽中獲得冠軍。這次的版本通常稱其為Inceptio

Java String 物件真的瞭解嗎?

String 物件的實現 String物件是 Java 中使用最頻繁的物件之一,所以 Java 公司也在不斷的對String物件的實現進行優化,以便提升String物件的效能,看下面這張圖,一起了解一下String物件的優化過程。 1. 在 Java6 以及之前的版本中 String物件是對 char 陣

查閱相關資料回答下列問題 如果為Liz開發問題賬戶分析系統準備如何進行需求分析第一步要做什麽? 認為目前需求分析材料欠缺哪些內容?

blog 頁面 name 需求分析 界面 tid 一個 credit 做什麽 .如果我為liz開發問題賬戶系統時,我第一步會做的是需求的征集與客戶進行溝通交流,準確了解和描述客戶需求,並能夠從客戶的語言中幫助用戶挖掘需求; 2.材料中還欠缺

如果是第一次接觸Python當中的面向物件請點選進來。

2018年7月29日15:49:49 今天有時間寫一篇關於Python面向物件入門的文章,其實面向物件是不屬於某一門程式語言的,而是一種程式設計思想,如果你是第一次接觸面向物件,你需要弄懂下面幾個問題: 什麼是面向物件,為什麼要使用面向物件? 什麼是類,為什麼要使用類?

Python 的匿名函式濫用嗎?

概念 我們從一個例子引入。 這裡有一個元素為非空字串的列表,按字串最後一個字母將列表進行排序。如果原列表是 ['abc', 'g', 'def'],則結果應該是 ['abc', 'def', 'g']。 很容易得到如下程式碼 我們發現, get_last_element 這個方法比較簡單,並且只用

Unity專案開發過程常見的問題遇到過嗎?

最近看到有朋友問一個unity遊戲開發團隊,需要掌握哪些知識之類的問題。事實上Unity引擎是一個很靈活的引擎,根據團隊開發遊戲型別的不同,對人員的要求也有差異,所以不能一概而論。但是,一些在Unity專案開發過程中常常會遇到的問題還是可以總結一下的。 下面我就來聊聊實際工作中,一個專案組可能會遇到的問題吧

面向物件請珍惜我們的地球。珍惜我們的環境。在這個宇宙很可能真的只有我們——人類在孤獨的生存和奮鬥。

首先,計算機程式是為了解決問題而存在的,那麼怎麼才能更好的解決問題呢?——模仿自然。簡單的模仿就是面向過程。把某個事情的過程寫出來。這種思想的缺點是擴充套件性很差。所以為了解決這個問題,發展出了面向物件的程式設計思想。所謂的面向物件,就是對自然界中事物的模仿,在這個模仿的基礎上去擴充套件。所以自然而然就有了面

python面向物件(簡單類的建立以及內建方法私有屬性和私有方法的使用)

一、什麼面向物件和麵向過程? 面向物件:--誰來做? 相比較函式,面向物件是更大的封裝,根據職責在一個物件中封裝多個方法 1.在完成某一個需求前,首先確定職責--要做的事(方法) 2.根據職責確定不同的物件,在物件內部封裝不同的方法(多個) 3.最後完成程式碼

開發的小錯誤大麻煩

方法 收集 使用 教訓 賦值 註意 成員 dto 發現 1.Webapi PUT 404問題 這個問題在網上試了很多方法,最後還是不行,而原來的項目則沒有問題,最後才發現iis 程序池必須要在集成模式下 (原來的項目的程序池名字和站點名不一樣,自己沒註意,而和站點名相同的

從美夢驚醒的時候該做什麽?

路徑 上一個 更多 二本 比較 前沿 都是 事情 數據 當你從美夢中驚醒的時候... 是繼續享受舒適的夢境? 還是爬起來,面對殘酷的現實? 自我介紹 211614331 我叫王誠榮 我平常最喜歡看動漫,追日劇,玩遊戲,看電影,還有了解各種科技產品... 說實話,我不怎

Scala面向物件

作者:林偉兵,叩丁狼高階講師。本文為原創文章,轉載請註明出處。     5. 面向物件 5.1 類的定義[屬性和方法] 可以用class來宣告一個類,並用new關鍵字來建立一個物件。 對於類中的全域性變數,必須在宣告的時候指定其預設值,否則就會報錯。

C++都有物件有麼?

聽過一個笑話, 程式設計師們互相聊天, 程式設計師A問:“為什麼C++比C麻煩那麼多?” 程式設計師B回答:“有了物件能不麻煩麼。” 在學習C/C++或者想要學習C/C++可以加入我們的學習交流QQ群:835257103,群內有學習資源,大家一起學習交流  情人節

面試常問的List去重問題都答對嗎?

面試中經常被問到的list如何去重,用來考察你對list資料結構,以及相關方法的掌握,體現你的java基礎學的是否牢固。 我們大家都知道,set集合的特點就是沒有重複的元素。如果集合中的資料型別是基本資料型別,可以直接將list集合轉換成set,就會自動去除重複的元素,這個就相對比較簡單。

Day046--JavaScript-- DOM操作, js面向物件, 定時

一. DOM的操作(建立,追加,刪除) parentNode 獲取父級標籤 nextElementSibling 獲取下一個兄弟節點 children 獲取所有的子標籤 <!DOCTYPEhtml><htmllang="en"><

python學習之【第十七篇】:Python面向物件(一)

1.什麼是類和類的物件? 類是一種資料結構,我們可以用它來定義物件,後者把資料值和行為特性融合在一起,類是現實世界的抽象的實體以程式設計形式出現。例項是這些物件的具體化。類是用來描述一類事物,類的物件指的是這一類事物的一個個體。例如:“人”就是一個類,而男人,女人,小孩等就是“人”這個類的例項物件;再比如“

PHP面向物件OOP的魔術方法

一、什麼是魔術方法: PHP為我們提供了一系列用__開頭的函式,這些函式無需自己手動呼叫,會在合適的時機自動呼叫,這類函式稱為魔術函式。 例如: function __construct(){} 在new一個新物件時自動呼叫此函式 二、PHP中都有那些魔術方法,以及它們的作用:

JavaScript基礎(3)-JS面向物件、定時器、BOM、位置資訊

一、建立物件的幾種常用方式、 1、使用Object或物件字面量建立物件;        a、使用Object()內建的建構函式來建立物件,例如: var student = new Object(); // 建立一個studen