我有一个Preference类(模块),用于几个不同的应用程序。基本上它是首选项的缓存,因此系统不必一直调用后端。它类似于缓存,但有一些额外的细节,如isPreferenceSelected,有一些辅助方法等。
问题在于我想在类中包含一个savePreference,以便任何使用它的人都可以覆盖该方法,无论是数据库,还是平面文件等。关键是这个模块只是没有我不在乎。问题是它不是一个抽象类,所以我不能override the static methods,即使它是,我也不想创建一百万个实例,因为我不想每次都加载首选项。我也无法创建abstract singleton。
因此我不知道该怎么做。以下是我想对评论做的事情的代码片段:
// Please ignore the missing Generics, etc.
public class Preference
{
private static HashMap preferences = new HashMap();
public static ...
// Some preferences are objects, such as images, etc.
public static setPreference(String name, Object value)
{
.. some helper code
preferences.put(name, value); // ignoring issues with if it already exists ;)
savePreference(name, value); // saves to database, flatfile, etc.
}
}
这是不同系统利用的核心类/代码。现在我想做的是在webapp,桌面应用程序等中说,能够在我的代码中使用这个类,如:
public someFunction(...)
{
.. do some cool code
Preference.savePreference("logoImage", image);
}
让savePreference()
方法不仅可以保存内存中的首选项,还可以将其保存到外部源。否则我到处都有savePreference()
我必须通过db调用savePreferenceToDB()
,FlatFile调用(例如savePreferenceToFlatFile()
)来跟踪它,依此类推。这非常容易出错,有人会忘记保存它。另外,使用这种类型的代码将保存洒到任何地方的永久存储代码真的没有意义,因为它应该只执行一次。还要记住,主模块不知道永久存储是数据库,xml文件,平面文件等。
提示:如果我做Preference.getInstance().savePreference()
因you can't abstract a singleton而无效。我无法创建一个静态方法savePreference(),因为it's not possible to override a static method。
我能看到的唯一选择是创建某种复杂的工厂模式,但这对我来说似乎有点过分。因此,我们将非常感谢任何建议。
答案 0 :(得分:7)
这听起来像是您的依赖注入(DI)容器应该处理的东西,而不是复杂的工厂模式。
也就是说,我认为你应该抛弃static
的用法,让其他应用程序为你的应用程序注入一个Preference
实例。如果您只是将Preference
作为参数在构造函数中用于依赖它的任何其他类,那么您可以在没有DI框架的情况下执行此操作。
编辑:让我举一个没有框架的依赖注入示例。参加以下课程:
public class Preference
{
private String userName;
public Preference(String userName)
{
this.userName = userName;
}
public void savePreference()
{
// Default implementation saves it to the screen. ;-)
System.out.println(userName);
}
}
public class Foo
{
private Preference p;
public Foo(Preference p)
{
this.p = p;
}
}
public class Bar
{
private Preference p;
public Bar(Preference p)
{
this.p = p;
}
}
public class Main
{
public static void main(String[] args)
{
Preference p = new Preference("Mike");
Foo f = new Foo(p);
Bar b = new Bar(p);
}
}
这是一个简单的例子,但它满足您的要求:
Preference
实例仅创建一次Preference
类可以由任何实现Main
类的人扩展,以实例化他们想要的任何类型的Preference
子类,如果他们想要将其保存在关系数据库中(或者无论)通过避免首先进行static
调用,您还可以对someFunction()
示例进行单元测试,而无需引入潜在的大型复杂偏好框架。相反,有人实现了一个模拟Preference
子类并将其传递给运行someFunction()
的类。您的代码将以这种方式更加可测试。
答案 1 :(得分:3)
@Mike说:
......我认为你应该抛弃静态的用法
@Stephane回复:
...静态方法的主要问题是什么?
这不仅仅是静态方法。它也是单例实例。
基本上,它们不灵活:
如您的问题所示,他们很难以其他方式做事。如果您没有使用静态方法和私有单例实例,则可以创建Preferences
接口和/或抽象基类,以及以不同方式加载和保存内存中首选项的实现。
静态实例往往会使测试更加困难。例如,如果您有一个使用Preferences
类的首选项UI,则无法使用“{1}}的”模拟“版本对UI类进行单元测试。 (或者至少,要做起来要困难得多。)
由于对特定命名类和特定实现的硬依赖性,静态函数往往难以重用代码。
静力学不是OO。这本质上不是一件坏事,但它确实意味着你不能利用OO的优秀属性......比如重写和多态......当你使用静态时。
如果您的应用程序中有大量这些静态方法/静态对象,则DI框架是一个很好的解决方案。但正如@Mike所说,在许多情况下,使用Factory方法并在构造函数中传递对象也会起作用。
您评论道:
我将其作为静态类的原因之一是因为首选项在启动时加载。之后,它们会在一个静态对象中保留在内存中。使用DI,每次创建对象时,我都必须从数据源将信息重新加载到内存中。这违背了拥有首选项对象的整个目的(它几乎就像一个带有好处的缓存)。
这不要求您使用静态实例。
使用DI(或通过构造函数显式连接实例),您不会多次创建Preferences对象。您创建一次,然后根据需要注入多次。
您当前的方法与静态方法之间有一半的距离,该方法包装了硬连线类的静态实例和完整的DI。这是一个最好被描述为静态持有者的东西;例如
Preferences
答案 2 :(得分:2)
我认为可能需要对你的设计进行一些修改(不幸的是,我的公寓里还没有合适的白板,所以我不能轻易地勾勒出符合的东西),但我立即想到{{3}你一说完这个:
问题在于我想在类中包含一个savePreference,以便任何使用它的人都可以覆盖该方法,无论是数据库,还是平面文件等。关键是这个模块只是没有我想要关心。
您可能有一个抽象的Preferences
类,它包含所有方法,但实现了保存(和加载)。从模式的意义上讲,这将是战略界面。您可以通过具体实现来处理不同类型的保存和加载。
答案 3 :(得分:1)
为您的偏好操纵类创建一个界面:
public interface PreferenceHandler { void savePreference(); void readPreference(); }
使用所有静态方法将类型为PreferenceHandler的实例传递给您的类。
在班级中调用该班级的方法。
尽管如此,并不喜欢所有那些静态方法。这可能就是你在这里遇到这么多问题的原因。如果您不想创建大量的副本,请创建一个工厂,为您提供该类的副本。但静态方法确实阻碍了代码的重用和扩展。或者使用像Spring这样的框架来管理这种类。