簡體   English   中英

在Java(靜態類)中重構Utility類的最佳方法是什么

[英]What is the best way to refactor Utility class in java (static classes)

我正在考慮重構我們的一些實用程序類(靜態類)。 靜態類很難測試,主要問題在於它使我們的代碼非常緊密地耦合在一起,具有很多依賴性。 用於重構的最佳設計模式是什么? 我曾考慮過使用構建器實現不可變的對象,但不確定

將此代碼視為1我想重構

public class UtilTest {

    public static boolean  isEligibleItem(Item item){
         if(isCondition1(item)){
             return isCondition2(item);
         }

         return false;
    }

    public static  boolean  isCondition1(Item item){
        //go to service that go to the data base  
        return false;
    }

    public static boolean  isCondition2(Item item){
        //go to service that go to the data base  
        return false;
    }
}

如果要測試isEligibleItem()方法,則需要模擬轉到db的2方法。 我不能這樣做,因為它們是靜態的。 我想避免使用Powermock

實用程序類反模式

人們說靜態方法很難測試的原因更多在於它如何隨着時間的推移將不相關的類緊密地耦合在一起,並且減少了內聚力以及引入了看不見的副作用 這三件事比一些單元測試揮手抱怨更為重要

測試互動

它比測試static方法本身更多的是測試與其他代碼的交互。 這才是Java真正需要將Functions作為第一類對象的地方。

在大多數情況下,只有static方法的類絕對是代碼的味道。 也有例外,但是這種反模式很容易被非面向對象語言的初學者和老手濫用。

規則例外-不可變

異常主要是那些可能被認為是標記為final類(例如, Immutable String缺少的東西。

擁有具有通用static方法的Strings類還不錯,因為String是不可變的(無副作用),並且您無法向String類添加任何內容,因此您沒有很多選擇。 Integer等也是如此, Guava具有這種命名約定,並且適用於這些不可變的對象。

副作用

static方法往往會帶來很多副作用。 拿一個對象並以某種不透明的方式操縱該對象的事物是不好的,更糟的是,當它們然后根據傳入的實例查找其他對象並對其進行操縱時,它們混淆了正在發生的事情並緊密耦合,並且內聚力低。

高凝聚力

關於緊密內聚的討論不多於耦合,但它同樣重要。 它們是同一枚硬幣的兩個面,因此忽略其中一個會導致另一面遭受損失。

這些static方法應該放在作為參數的類上,並與這些類緊密耦合。 在這種情況下,為什么它們不在Item類上?

一旦添加另一個采用SomeOtherItem static方法,您就將不相關的類間接耦合在一起。

補救此問題的最簡單方法是將事物移動到更靠近它們所屬位置的Item類。

工廠/提供者模式

如果您確實有一些general事情,或者由於某些final原因或其他原因而無法將其添加到類中,那么使用接口和Provider模式是使用Factory生成Provider實例的最佳方法,甚至更好。

然后,您可以使用Guice東西注入所需的任何實現,具體取決於它是否為測試。

甚至還有一個混合Utility模式,可以從Provider中注入實現,這將為您Provider static方法的便利性以及不使用它的靈活性和可維護性。

對可測試的設置的簡單翻譯是:

public class UtilTest {

    private final MyDatabaseService service;

    public UtilTest(MyDatabaseService service) {
       this.service = service;
    }

    public boolean  isEligibleItem(Item item){
         if(isCondition1(item)){
             return isCondition2(item);
         }
         return false;
    }

    public boolean isCondition1(Item item){
        this.service.goToDataBase();
        return false;
    }

    public boolean isCondition2(Item item){
        this.service.goToDataBase2();
        return false;
    }
}

這並不能消除所有問題,但這只是一個開始,您可以使用模擬的數據庫服務來測試您的類。

如果您想進一步推動事情,可以聲明一個接口,其中包含您希望UtilTest公開的所有方法(您可能還希望重命名該類...),並使UtilTest實現它。 應該將所有使用UtilTest的代碼重寫為使用該接口,然后您可以完全直接地模擬UtilTest 是否值得,在很大程度上取決於UtilTest的實際復雜UtilTest 如果它執行的任務相對簡單,您可能會認為它比它值得的麻煩。 但是,如果其中要進行一些繁重的處理,則您肯定希望使其易於模擬。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM