静态到非静态的重构,不能同时存在?

huangapple 未分类评论52阅读模式
英文:

Static to non-static refactoring, can't have both?

问题

我面临一个重构情况,但我找不到一个优雅的解决方案...

免责声明:
请记住,我正在简化这个示例以减少混乱,并且不会透露我不允许透露的事情 静态到非静态的重构,不能同时存在?
因此,请不要认为这是我庞大的代码库中的唯一代码,并提供会绕过限制或更改设计的解决方案,我提到的设计部分由于外部约束而不能更改。

事实:
我有一个实用类,其中有一堆静态方法,它们利用一个单例资源:

  1. public final class Utility
  2. {
  3. private static final Resource RES = Resource.getInstance();
  4. private Utility() {} // 防止实例化Utility
  5. public static boolean utilMethodOne() { return RES.isSomething(); }
  6. public static int utilMethodTwo() { RES.getNumThings(); }
  7. ...
  8. public static void utilMethodInfinity() { ... }
  9. }

Utility位于一个库JAR中,被大代码库中的几个应用程序使用 —— 假设有大约10000次调用它的静态方法,例如:if(Utility.utilMethodOne()) { ... }

Resource是来自另一个库JAR的外部类。

Resource还有一个方法Resource.getInstance(String name),它将返回一个命名实例,该实例可能与基于名称的不同底层资源相关联(在内部,它将命名的资源保存在一个Map<String, Resource>中)。

Resource.getInstance() 返回等效于 Resource.getInstance(""),即默认实例。

情况:
现在,需要增强Utility,以便针对多个资源之一执行操作,因此我的计划是将Utility变成一个可实例化的类,具有一个非静态的Resource成员变量。大致如下:

  1. public final class Utility
  2. {
  3. private Resource res;
  4. public Utility(String resName)
  5. {
  6. this.res = Resource.getInstance(resName);
  7. }
  8. public boolean utilMethodOne() { return this.res.isSomething(); }
  9. public int utilMethodTwo() { this.res.getNumThings(); }
  10. ...
  11. public void utilMethodInfinity() { ... }
  12. }

现在,这一切都很好,我可以开始创建访问指定资源的Utility对象,而不仅仅是默认资源。然而,正如我所提到的,现在有10-100K个方法调用是无效的,因为它们在调用静态方法!

问题:
我的计划是保留Utility中的静态方法,并让它们使用来自Resource的默认实例,同时为实例化的Utility对象添加非静态变体,以使用它们的“本地”资源引用。可能我不能两全其美:

  1. public static boolean utilMethodOne() { return RES.isSomething(); }
  2. public boolean utilMethodOne() { return this.res.isSomething(); }

可能我不能两全其美:

  1. 错误:方法utilMethodOne()在类Utility中已经定义
  2. public static boolean utilMethodOne(String sql)

所以,我似乎要么...

  1. 引入一个全新的BetterUtility类,用于那些希望使用命名资源的地方。
  2. 更新10000个地方以实例化和使用经过修改的Utility对象。
  3. ...?(提示:这就是您的建议发挥作用的地方!)

在让人不满意的1或2之前,我真的不喜欢1或2,出于各种原因,所以在确定没有更好的3选项之前,我需要确保没有更好的3选项。在这种情况下,是否有办法保留一个单一的类,可以提供静态和非静态接口?

2020-06-01更新:

我逐渐意识到这个神奇的选项3是不存在的。所以,在我的原始两个选项中,我认为#2是最好的,因为它只需要一次努力,就可以将其放在一边并完成。同时在设计中加入了您的一些建议。

所以现在我在这个问题上有了方向,我只剩下[希望只有]一个更重要的决定...

  1. 更新所有的调用以创建新的对象
  1. // 对于一次性调用,内联处理
  2. boolean foo = new Utility("res1").utilMethodOne();
  3. // 或者在多次使用时,重用对象
  4. Utility util = new Utility("res1");
  5. boolean foo = util.utilMethodOne();
  6. int bar = util.utilMethodTwo();
  7. ...

考虑到使用量和频率,这似乎是很多创建短暂对象的浪费行为。

  1. 遵循Resource本身使用的模式,创建自己的Utility的命名单例映射(与各自命名的Resource一一对应)
  1. public final class Utility
  2. {
  3. private static final Map<String, Utility> NAMED_INSTANCES = new HashMap<>();
  4. private Resource res;
  5. private Utility(String resName)
  6. {
  7. this.res = Resource.getInstance(resName);
  8. }
  9. public static Utility getInstance(String resName)
  10. {
  11. synchronized(NAMED_INSTANCES)
  12. {
  13. Utility instance = NAMED_INSTANCES.get(resName);
  14. if(instance == null)
  15. {
  16. instance = new Utility(resName);
  17. NAMED_INSTANCES.put(resName, instance);
  18. }
  19. return instance;
  20. }
  21. }
  22. public boolean utilMethodOne() { return this.res.isSomething(); }
  23. public int utilMethodTwo() { this.res.getNumThings(); }
  24. ...
  25. public void utilMethodInfinity() { ... }
  26. }
  27. // 现在的调用可以使用
  28. Utility.getInstance("res1")
  29. // 替代
  30. new Utility("res1")

因此,实质上这归结为对象创建与每次使用时的同步 + 映射查找之间的权衡。这可能有点过早优化,但长期来看,我可能必须坚持这个决定。

2020-06-29更新:

不想留下“互联网死胡同”...
我最终确实按照上面描述的方式更新了

英文:

I have a refactoring situation that I cannot find an elegant solution for...

Disclaimer:
Keep in mind that I am oversimplifying this example to reduce clutter, and not disclose things I am not allowed to disclose 静态到非静态的重构,不能同时存在?
As such, please do not assume that this is the ONLY code in my giant codebase, and offer solutions that cut corners or change parts of the design that I mention cannot be changed due to outside constraints.

The facts:

I have a utility class, it has a bunch of static methods, they utilize a singleton resource:


  1. public final class Utility
  2. {
  3. private static final Resource RES = Resource.getInstance();
  4. private Utility() {} // Prevent instantiating Utility
  5. public static boolean utilMethodOne() { return RES.isSomething(); }
  6. public static int utilMethodTwo() { RES.getNumThings(); }
  7. ...
  8. public static void utilMethodInfinity() { ... }
  9. }

Utility is in a library JAR that is used by several applications in a large codebase -- let's say on the order of 10,000 calls to its static methods, e.g.: if(Utility.utilMethodOne()) { ... }

Resource is an outside class from another library JAR.

Resource also has a method Resource.getInstance(String name) that will return a named instance, which may relate to a different underlying resource based on the name (internally it keeps the named resources in a Map<String,Resource>).

Resource.getInstance() returns the equivalent of Resoruce.getInstance(""), aka the default instance.

The situation:

The Utility needs to be enhanced to now execute against one of several resources, so my plan is to make the Utility an instantiable class with a non-static Resource member variable. Something like this:


  1. public final class Utility
  2. {
  3. private Resource res;
  4. public Utility(String resName)
  5. {
  6. this.res = = Resource.getInstance(resName);
  7. }
  8. public boolean utilMethodOne() { return this.res.isSomething(); }
  9. public int utilMethodTwo() { this.res.getNumThings(); }
  10. ...
  11. public void utilMethodInfinity() { ... }
  12. }

Now all this is great, and I can start creating Utility objects that access their specified resource instead of just the default one. However, as I mentioned, there are 10-100K method calls that are now invalid as they were calling static methods!

The problem:

My plan was to keep the static methods in Utility, and have them use the default instance from Resource, while adding in non-static variants for the instantiated Utility objects that use their "local" resource reference.


  1. // Best of both worlds:
  2. public static boolean utilMethodOne() { return RES.isSomething(); }
  3. public boolean utilMethodOne() { return this.res.isSomething(); }

Maybe I can't have my cake & eat it too:


  1. error: method utilMethodOne() is already defined in class Utility
  2. public static boolean utilMethodOne(String sql)

So it seems I am going to have to either...

  1. Introduce a whole new BetterUtility class for places that want to use the named-resources.
  2. Update 10,000 places to instantiate & use the revised Utility object.
  3. ...? (hint: this is where your suggestions come in!)

I really don't like 1 or 2 for a variety of reasons, so I need to ensure there is no better 3 option before settling. Is there any way to retain a single class that can provide both the static & non-static interfaces in this case?

UPDATE 2020-06-01:

I am coming to the realization that this magical option 3 doesn't exist. So out of my original two options I think #2 is best as it's just one time "just get it out of the way and be done with it" type effort. Also incorporated some of your suggestions in the design(s).

So now that I have a direction on this, I am left with [hopefully only] one more key decision...

  1. Update all the calls to create new objects

  1. // For a one-off call, do it inline
  2. boolean foo = new Utility(&quot;res1&quot;).utilMethodOne();
  3. // Or when used multiple times, re-use the object
  4. Utility util = new Utility(&quot;res1&quot;);
  5. boolean foo = util.utilMethodOne();
  6. int bar = util.utilMethodTwo();
  7. ...

Given the amount/frequency of usage, this seems like a whole lot of wasted efforts creating short-lived objects.

  1. Follow the pattern that Resource itself uses, creating my own named-singleton map of Utilities (1:1 with their respectively named Resource)

  1. public final class Utility
  2. {
  3. private static final Map&lt;String,Utility&gt; NAMED_INSTANCES = new HashMap&lt;&gt;();
  4. private Resource res;
  5. private Utility(String resName)
  6. {
  7. this.res = Resource.getInstance(resName);
  8. }
  9. public static Utility getInstance(String resName)
  10. {
  11. synchronized(NAMED_INSTANCES)
  12. {
  13. Utility instance = NAMED_INSTANCES.get(resName);
  14. if(instance == null)
  15. {
  16. instance = new Utility(resName);
  17. NAMED_INSTANCES.put(resName, instance);
  18. }
  19. return instance;
  20. }
  21. }
  22. public boolean utilMethodOne() { return this.res.isSomething(); }
  23. public int utilMethodTwo() { this.res.getNumThings(); }
  24. ...
  25. public void utilMethodInfinity() { ... }
  26. }
  27. // Now the calls can use
  28. Utility.getInstance(&quot;res1&quot;)
  29. // In place of
  30. new Utility(&quot;res1&quot;)

So essentially this boils down to object creation vs. a synchronization + map lookup at each usage. Probably a little bit of premature optimization here, but I'll probably have to stick with this decision long term.

UPDATE 2020-06-29:

Didn't want to leave an "Internet dead end" here...
I did eventually get all the call sites updated as described above (including option #2 from the 2020-06-01 update). It has made it through all testing and been running in production for a week or so now in various applications.

答案1

得分: 0

看起来你可能想将"Utility"转变为一个单例映射,该映射将具有相同的静态方法,这些方法在函数调用时不需要任何参数,就像你现在所拥有的一样。

这个单例将支持一个静态方法,用于添加新资源,然后你将把它添加到映射中。

此外,你可以重载现有的方法,也接受一个资源名称的参数,然后从映射中使用特定的资源,否则将使用映射中的默认条目。

英文:

It seems that you may want to turn the Utility into a singleton map that will have the same static methods that access the singleton without any arguments on for the function invocations (just like you have now)

The singleton will support a static method of adding a new resource, you will then add it to the map.

In addition you can overload the existing methods to also accept an argument resource name, that will then use a particular resource from the map, otherwise will use the default entry from the map.

答案2

得分: 0

保留旧方法和新方法的静态性。

  1. private static final String DEFAULT = "RESOURCE1";
  2. private static Map<String, Resource> resources = new HashMap();
  3. static{
  4. // 初始化所有资源
  5. }
  6. public static boolean utilMethod() { return resources.get(DEFAULT).isSomething(); }
  7. public static boolean utilMethod(String resourceName) { return resources.get(resourceName).isSomething(); }
英文:

Keep the old methods and the new methods static.

  1. private static final String DEFAULT = &quot;RESOURCE1&quot;;
  2. private static Map&lt;String, Resource&gt; resources = new HashMap();
  3. static{
  4. // initialize all resources
  5. }
  6. public static boolean utilMethod() { return resources.get(DEFAULT).isSomething(); }
  7. public static boolean utilMethod(String resourceName) { return resources.get(resourceName).isSomething(); }

huangapple
  • 本文由 发表于 2020年5月30日 03:31:36
  • 转载请务必保留本文链接:https://java.coder-hub.com/62093410.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定