简体   繁体   English

“主人偏好”课是一个好主意吗?

[英]Is a “master preferences” class a good idea?

I have a class that manages user preferences for a large software project. 我有一个类来管理大型软件项目的用户首选项。 Any class in the project that may need to set or retrieve a user preference from a persistent store is to call the static methods on this class. 项目中可能需要从持久性存储设置或检索用户首选项的任何类都是在此类上调用静态方法。 This centralized management allows the preferences to be completely wiped programmatically - which would be impossible if each pref was handled close to its use code, sprinkled throughout the project. 这种集中管理允许以编程方式完全擦除首选项 - 如果每个pref都是在接近其使用代码的情况下处理的话,这将是不可能的。

I ran into another implication of the centralization design in the course of this. 在此过程中,我遇到了集中化设计的另一个含义。 The software has a public API. 该软件具有公共API。 That API can be provided by itself in a jar. 该API可以在jar中自行提供。 Classes in that API might refer to the pref management class. 该API中的类可能引用pref管理类。 So, the pref manager has to go in the API jar. 因此,pref管理器必须进入API jar。

Each preference might have a default value. 每个首选项可能都有一个默认值。 Upon software startup, that default might be computed. 软件启动时,可能会计算该默认值。 The algorithm depends on the preference, and thus tends to reside near the use code. 该算法取决于偏好,因此倾向于驻留在使用代码附近。 So if the pref manager needs to come up with a default, it calls the class in question. 因此,如果pref管理器需要提供默认值,它会调用相关的类。

But now that pref manager has become an "octopus class", sucking in all sorts of classes into the API jar that shouldn't be there. 但是现在pref经理已成为一个“章鱼类”,将各种类型的类吸入到不应该存在的API jar中。 If it doesn't, then programs using the API jar quickly run into ClassDef exceptions. 如果没有,那么使用API​​ jar的程序很快会遇到ClassDef异常。 If it does, then the API jar is now bloated, as each of those other classes may refer to still others. 如果确实如此,则API jar现在变得臃肿,因为其他每个类都可能引用其他类。

In general, do other Java programmers manage their preferences with a centralized class? 通常,其他Java程序员是否使用集中式类来管理他们的首选项?

Does it make sense to distribute that static pref management class as part of a public API? 将静态pref管理类作为公共API的一部分进行分发是否有意义?

Should that pref manager be the keeper of the code for determining defaults? 该pref经理是否应该成为确定默认值的代码的守护者?

IMHO, I think that the answer to your first question is "yes" and "no". 恕我直言,我认为你的第一个问题的答案是“是”和“否”。

Preferences are commonly handled as a centralized class, in the sense that the class is a "sink" for many classes in the project. 首选项通常作为集中式类处理,因为类是项目中许多类的“接收器”。 Trying to do it closer to the calling code means that if the same preference is later useful elsewhere, you are in trouble. 尝试更接近调用代码意味着如果相同的首选项稍后在其他地方有用,则会遇到麻烦。 In my experience, trying to put the preferences "too close" also results in a very inconsistent handling. 根据我的经验,尝试将偏好设置为“太近”也会导致处理非常不一致。

That being said, it is often preferable to use multiple preference classes or "preference set", each supporting a module or submodule. 话虽如此,通常最好使用多个偏好类或“偏好集”,每个偏好类或“偏好集”支持模块或子模块。 If you look at the main components of your architecture, you will often find that the set of preferences can be logically partitioned. 如果查看体系结构的主要组件,通常会发现可以对这些首选项进行逻辑分区。 This reduces the mess in each preference class. 这减少了每个偏好类中的混乱。 More importantly, it will allow you to split your program into multiple jars in the future. 更重要的是,它允许您将来将程序拆分为多个罐子。 Now, the "default value" calculators can be placed in the module but still in a global enough area. 现在,“默认值”计算器可以放在模块中,但仍然位于足够全局的区域。

I would also suggest not setting preferences directly as static methods, but rather using some getInstance() like operation to obtain a shared instance of the preferences manage, and then operating on it. 我还建议不要将首选项直接设置为静态方法,而是使用一些类似getInstance()的操作来获取首选项管理的共享实例,然后对其进行操作。 Depending on your semantics, you may want to lock that object for a while (eg, while the user is editing preferences in a UI) and this is easier if you have an actual object. 根据您的语义,您可能希望锁定该对象一段时间(例如,当用户在UI中编辑首选项时),如果您有实际对象,这将更容易。

For your other questions, I would say that your public API should have a way of letting users change preferences, but only if you can document well enough what the results of these changes could be. 对于您的其他问题,我会说您的公共API应该有一种让用户更改首选项的方法,但前提是您可以很好地记录这些更改的结果。

If you use a single API function to get the "reference manager", you can give users the possibility of providing their own "default values calculator". 如果您使用单个API函数来获取“参考管理器”,则可以为用户提供自己的“默认值计算器”。 The preference manager will ask this calculator first before resorting to the one you have provided by default. 首选项管理器将首先询问此计算器,然后再使用您默认提供的计算器。

Can't you just handle preferences in a really generic way? 难道你不能以一种非常通用的方式处理偏好吗? You'd then just use the preference manager to handle the persistence. 然后,您只需使用首选项管理器来处理持久性。 So from a class you'd just say to the preference manager PreferenceManager.setPreference(key, value) and it doesn't care what it's saving in terms of the semantics of the data. 所以从一个类你只需要说一下偏好管理器PreferenceManager.setPreference(key,value),它就不关心它在数据语义方面的节省。

Or am I simplifying this too much? 还是我太简单了?

I'm no Java Dev, but as far as the whole "octopus class" thing goes, can you not just supply an interface to the jar and connect the calls between the interface and the prefs manager at runtime, using the application configuration file to determine the prefs manager to instantiate? 我不是Java Dev,但就整个“章鱼类”而言,你能不能只为jar提供一个接口,并在运行时连接接口和prefs管理器之间的调用,使用应用程序配置文件确定prefs管理器实例化?

Something like the .NET provider pattern? 像.NET提供者模式的东西?

That way you can decouple your jar from the prefs manager. 这样你就可以将你的jar与prefs管理器分离。

You might want to look at Cocoa's NSUserDefaults class for inspiration. 您可能希望查看Cocoa的NSUserDefaults类以获得灵感。 It handles the problem you describe by having several layers of preferences, called domains. 它通过具有多层首选项(称为域)来处理您描述的问题。 When you look up the value for a key, such as "PrintUsingAllCaps", it first checks for a value in the user's local domain. 当您查找键的值(例如“PrintUsingAllCaps”)时,它首先检查用户本地域中的值。 If it isn't found there, it can check the system-wide domain, or a network-level domain, and so on. 如果在那里找不到它,它可以检查系统范围的域,或网络级域,等等。

The absolute last place it checks is called the "Registration Domain", which is basically where hard coded defaults are supposed to go. 它检查的绝对最后一个位置称为“注册域”,这基本上是硬编码默认值应该去的地方。 So, at any point in my code, I can write a preference into the registration domain, and NSUserDefaults will only serve that value if the user hasn't overridden it. 因此,在我的代码中的任何一点,我都可以在注册域中写一个首选项,并且NSUserDefaults只会在用户没有覆盖它的情况下提供该值。

So, in your case, you could provide a method for classes to set a default value for a key before it accesses the (possibly) user defined value. 因此,在您的情况下,您可以为类提供一种方法,以便在访问(可能)用户定义的值之前为其设置默认值。 The preferences class doesn't have to know anything about the classes it is serving. 首选项类不必知道它所服务的类的任何信息。

As somebody else suggested, if you need something more sophisticated, you could set a DefaultValueProvider callback object instead of a straight value. 正如其他人建议的那样,如果你需要更复杂的东西,你可以设置一个DefaultValueProvider回调对象而不是一个直值。

The JSR-10 ( java.util.prefs.* ) API uses a factory method with a Class<?> parameter to create Preferences instances. JSR-10( java.util.prefs.* )API使用带有Class<?>参数的工厂方法来创建Preferences实例。 That way the API can store preferences from different classes belonging to the same package in a single file. 这样,API可以将属于同一包的不同类的首选项存储在单个文件中。

I deleted my first answer since I misunderstood what the author was asking. 我删除了第一个答案,因为我误解了作者的要求。

To actually address the actual question--it feels like your desire to place preferences (and the calculation of the default values) with the code that uses them makes sense. 要真正解决实际问题 - 感觉就像您希望将偏好(以及默认值的计算)与使用它们的代码放在一起是有意义的。

Could you meet both requirements by using a preferences container class for each area that follows a pattern for that area, but having it register with a "Global" preference object collection? 您是否可以通过为每个区域使用首选项容器类来满足这两个要求,该区域遵循该区域的模式,但是让它注册“全局”首选项对象集合?

Your global collection could do things like iterate over each set of preferences and reset it to defaults but your preferences themselves would still be locally defined and maintained so that it doesn't spider out into other parts of the code. 您的全局集合可以执行诸如迭代每组首选项并将其重置为默认值的操作,但您的首选项本身仍将在本地定义和维护,以便它不会分散到代码的其他部分。

The only problem I can see is that if you allow the preference object to register itself when instantiated, you run the risk of trying to "reset to defaults" with some of the preferences not instantiated yet. 我能看到的唯一问题是,如果您允许首选项对象在实例化时注册自己,那么您可能会尝试“重置为默认值”,而某些首选项尚未实例化。

I suppose this could be fixed by having the main "preference" class instantiate all the others, then any piece of code could retrieve it's local preference object from the central one though a static getter. 我想这可以通过让主“首选”类实例化所有其他类来解决,然后任何一段代码都可以通过静态getter从中央首选项中检索它的本地首选项对象。

This seems to be a lot like how some loggers work. 这似乎与一些记录器的工作方式非常相似。 There is a central mechanism for maintaining log levels, output streams and such, but each class has it's own instance of a "log" method and logs to it. 有一个维护日志级别,输出流等的中央机制,但每个类都有自己的“日志”方法实例并记录到它。

I hope this was more on target. 我希望这更符合目标。 Oh, I also agree with the accepted answer, don't ever make all your methods public static, always use a getter--you'll be glad you did some day. 哦,我也同意接受的答案,不要让你的所有方法都是静态的,总是使用吸气剂 - 你会很高兴你有一天做的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM