簡體   English   中英

接口上用於強制一致性的靜態方法的替代方法

[英]Alternatives to static methods on interfaces for enforcing consistency

在Java中,我希望能夠定義標記接口,強制實現提供靜態方法。 例如,對於簡單的文本序列化/反序列化,我希望能夠定義一個看起來像這樣的接口:

public interface TextTransformable<T>{

  public static T fromText(String text);

  public String toText();

由於Java中的接口不能包含靜態方法(如許多其他帖子/線程中所述: 此處此處此處此代碼不起作用。

然而,我正在尋找的是表達相同意圖的一些合理范例,即對稱方法,其中一個是靜態的,並由編譯器強制執行。 現在我們能想到的最好的是某種靜態工廠對象或通用工廠,這兩者都不是真正令人滿意的。

注意:在我們的例子中,我們的主要用例是我們有許多很多“值對象”類型 - 枚舉或其他具有有限數量值的對象,通常不會超出其值,並且我們解析/ de -parse數秒鍾的時間,所以實際上關心重用實例(如Float,Integer等)及其對內存消耗的影響/ gc

有什么想法嗎?

編輯1:為了消除一些混亂 - 我們有許多不同的對象適合這種模式 - 實際上我們正試圖為具有2種語義的調用者提出一些優雅的東西:

  • 作為契約的接口 - 訪問的統一性(例如TextTransformable作為一種能力)
  • 需要通過子類/實現來實現(例如,強制它們實現自己的轉換

就我們對Flyweight,Factories的看法而言 - 它們都是我們考慮過的選項,我們真的試圖看看我們是否能找到更優雅的東西,而不是依賴於JavaDoc說“實現工廠並委托調用它,或按慣例在XXX地點公開“

這非常適合Flyweight 這基本上就是你想用靜力學來完成的。 在如何為Flyweight對象提供服務以便您不創建數千個對象方面,這里有一些想法。

一個是工廠,你說你想到並拒絕,雖然你沒有說明原因(所以任何其他想法可能會遇到同樣的問題)所以我不會進入它。

另一種方法是使值類型具有可以為其轉換器提供服務的方法。 像這樣的東西:

 public class ValueType {
       public static final TextTransformable<ValueType> CONVERT = ....
 }

然后像這樣使用它:

 ValueType value = ValueType.CONVERT.fromText(text);

 String text = ValueType.CONVERT.toText(value);

現在這並沒有強制所有ValueType都通過相同的機制提供他們的轉換器,為此我認為你需要某種工廠。

編輯:我想我不知道你發現什么工廠不優雅,但我認為你專注於打電話,所以這對你有什么感受:

  ValueType value = getTransformer(ValueType.class).fromText(text);

以上可以通過工廠的靜態導入和具有如下簽名的方法來完成:

   public static <T> TextTransformable<T> getTransformer(Class<T> type) {
         ...
   }

找到合適的變換器的代碼不一定是最漂亮的,但從調用者的角度來看,一切都很好。

編輯2:進一步思考,我看到你想要控制對象構造。 你真的不能這樣做。 換句話說,在Java中,您無法強制實現者使用或不使用工廠來創建其對象。 他們總是可以公開一個公共構造函數。 我認為你的問題是你對執行建設的機制不滿意。 如果這種理解是正確的,那么可能會使用以下模式。

您創建一個只包含私有構造函數的對象,它包裝您的值類型。 該對象可以具有泛型類型參數,以了解它包裝的值類型。 此對象使用靜態工廠方法進行實例化,該方法使用工廠接口創建“實際”值對象。 使用該對象的所有框架代碼僅將此對象作為參數。 它不直接接受值類型,並且如果沒有值類型的工廠,則無法實例化該對象。

這種方法的問題在於它是非常有限的。 只有一種方法可以創建對象(工廠界面支持的對象),並且使用值對象的能力有限,因為處理這些文本元素的代碼僅通過此對象進行了有限的交互。

我猜他們說沒有軟件問題無法通過額外的間接層解決,但這可能是一個過頭的橋梁。 至少它值得深思。

只是一個要考慮的想法:您可以將轉換邏輯與對象本身分開,然后您有一組固定的轉換器,實現以下接口:

public interface Transformer<T>{ 

  public T fromText(String text); 

  public String toText(T obj); 
}

您的實際數據類可以有一個方法getTransformer(),它為它們返回正確的轉換器。

一個完全不同的方法(以及一個丑陋的黑客,就此而言)是讓接口有一個返回方法的方法。

public interface MyInterface{
    Method getConvertMethod();
}

現在您的客戶端代碼可以做到

yourInterface.getConvertMethod().invoke(objectToBeConverted);

這是非常強大的,但API設計非常糟糕

肖恩

如果您使用的是Java 5或更高版本,則可以使用枚舉類型 - 根據定義,所有這些都是單例。 所以你可以這樣:

public enum MyEnumType {
    Type1,
    Type2,
    //....
    TypeN;

    //you can do whatever you want down here
    //including implementing interfaces that the enum conforms to.

}

這樣,內存問題就會消失,您可以實現單個行為實例。


編輯:如果您無法訪問枚舉(1.4或更早版本),或者由於某些其他原因,它們不適合您,我建議使用Flyweight模式實現。

只是一個不同的想法。 不適用於所有情況,但也許可以幫助其他人。

您可以使用abstract class作為interfaceconcrete classes之間的橋梁。 抽象類允許靜態方法以及契約方法定義。 例如,您可以查看Collection API,其中Sun實現了幾個抽象類,而不是從零所有具體類中進行強力編碼。

在某些情況下,您可以通過抽象類替換接口。

正如@aperkins所說,你應該使用枚舉。

枚舉基類Enum提供了一個將字符串轉換為實例的valueOf方法。

enum MyEnum { A, B, C; }
// Here's your toText
String strVal = MyEnum.A.getName();
// and here's your fromText
MyEnum value = MyEnum.valueOf(MyEnum.class, strVal);

更新:對於那些枚舉,這可能會做你需要的。 它使用反射,因此您只需要在必須處理遺留值的枚舉上實現EnumHelper。

/** Enums can implement this to provide a method for resolving a string
  * to a proper enum name.
  */
public interface EnumHelp
{
    // Resolve s to a proper enum name that can be processed by Enum.valueOf
    String resolve(String s);
}

/** EnumParser provides methods for resolving symbolic names to enum instances.
  * If the enum in question implements EnumHelp, then the resolve method is
  * called to resolve the token to a proper enum name before calling valueOf.
  */
public class EnumParser
{
    public static <T extends Enum<T>> T fromText(Class<T> cl, String s) {
        if (EnumHelp.class.isAssignableFrom(cl)) {
            try {
                Method resolve = cl.getMethod("resolve", String.class);
                s = (String) resolve.invoke(null, s);
            }
            catch (NoSuchMethodException ex) {}
            catch (SecurityException ex) {}
            catch(IllegalAccessException ex) {}
            catch(IllegalArgumentException ex) {}
            catch(InvocationTargetException ex) {}
        }
        return T.valueOf(cl, s);
    }

    public <T extends Enum<T>> String toText(T value)
    {
        return value.name();
    }
}

您似乎需要將工廠與創建的對象分開。

public interface TextTransformer<T> {
    public T fromText(String text);
    public String toText(T source);
}

您可以根據需要注冊TextTransformer類:

public class FooTextTransformer implements TextTransformer<Foo> {
    public Foo fromText(String text) { return ...; }
    public String toText(Foo source) { return ...; }
}

如果你想讓FooTextTransformer成為一個單例,你可以使用像Spring這樣的容器來強制執行。 谷歌啟動了一個項目,從他們的代碼庫中刪除所有手動強制執行的單例,但如果你想做一個老式的單例,你可以使用一個實用程序類:

public class TextTransformers {
    public static final FooTextTransformer FOO = new FooTextTransformer();
    ...
    public static final BarTextTransformer BAR = new BarTextTransformer();
}

在您的客戶端代碼中:

Foo foo = TextTransformers.FOO.fromText(...);
...
foo.setSomething(...);
...
String text = TextTransformers.FOO.toText(foo);

這只是我頭腦中的一種方法。

暫無
暫無

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

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