[英]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種語義的調用者提出一些優雅的東西:
就我們對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
作為interface
和concrete 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.