Java 重构使用全局变量的类

Java 重构使用全局变量的类,java,refactoring,global-variables,Java,Refactoring,Global Variables,我正在研究一些从全局变量获取部分配置的类,例如 class MyClass { public void MyClass(Hashtable<String, String> params) { this.foo = GlobalClass.GLOBALVAR.get("foo"); this.bar = GlobalClass.GLOBALVAR.get("bar"); this.params = params; } }

我正在研究一些从全局变量获取部分配置的类,例如

class MyClass {
    public void MyClass(Hashtable<String, String> params) {
        this.foo = GlobalClass.GLOBALVAR.get("foo");
        this.bar = GlobalClass.GLOBALVAR.get("bar");
        this.params = params;
    }
}

除了我的方法,还有什么好的重构方法吗?我的另一个想法是使用工厂方法,但恐怕我会遇到多态替换问题。

您的方法稍有不同,就是在类中有一个GLOBALVAR类型的对象,并使用它而不是实际的全局(重构应该是一个简单的搜索/替换)。您可以将新变量默认为实际的全局变量,并为测试提供覆盖。

您的类应该在构造函数中获取其所有依赖项。最好不要创建无效或未初始化的类实例。将
foo
bar
设为private和final,并在构造函数中设置它们。

我认为应该引入一个接口,在全局变量集合及其使用者之间设置一个抽象层

interface GlobalVars {
   String get(String key);
}
public static MyClass newMyClass(Map<String, String> params) {
   return new MyClass(GlobalClass.GLOBAL_VAR, params);
}
您应该引入一个范围有限的构造函数,可能是
package private

MyClass(GlobalVars globals, Map<String, String> params) {
   // create the object
}
通过这种设计,您可以通过显式调用构造函数,在同一个包中的单元测试中传入
GlobalVars
的模拟实现

public static MyClass newMyClass(Map<String, String> params) {
   return new MyClass(GlobalClass.GLOBAL_VAR, params);
}
附录:由于
params
似乎是一个必填字段,我肯定会将其设置为
final
,并避免使用添加变体来覆盖它们的方法

private final Map<String, String> params;

这个GlobalClass.GLOBALVAR应该被分解成逻辑单元。这样就更容易为单元测试创建模拟对象。例如,在我的CAD/CAM金属切削应用程序中,我有一个材料列表、一个板材尺寸列表、零件嵌套参数等

我并没有一个庞大的变量列表,而是将它们放入一个巨大的AppParameter类中。他们都挂了一件物品。对于涉及特定PartNestingParmeters的单元测试,我将直接访问ShopStandards.PartNestingParmeters=new MockPartNestingParameterTest114()。测试将在未意识到零件嵌套参数是实体模型的情况下运行。另外,这使我不必为了测试正确地设置ShopStandard而做几十个作业


我们甚至实现了更自动化,其中许多模拟加载来自初始开发期间测试运行期间保存的文件。它让您的现有代码在不修改的情况下工作,并允许您向子类添加新的构造函数。一旦所有子类都有了新的构造函数,并且所有对旧构造函数的调用都消失了,您就可以摆脱GlobalClass和使用它的构造函数。然后,希望您能够清理GLOBALVAR(我代码中的Car类)

import java.util.Hashtable;
类MyClass
{
私人决赛福福;
私人终审律师;
私有最终哈希表参数;
公共MyClass(最终哈希表参数)
{
这(params,GlobalClass.GLOBALVAR);
}
//新增构造函数
公共MyClass(最终哈希表参数,
最终FooBar(FooBar)
{
this.foo=fooBar.getFoo();
this.bar=fooBar.getBar();
this.params=params;
}
}
类MySubClass
扩展MyClass
{
public MySubClass(最终哈希表参数)
{
超级(params);
}
//新增构造函数
public MySubClass(最终哈希表参数,
最终FooBar(FooBar)
{
超级(params,fooBar);
}
}
//不变的
类全局类
{
公共静态汽车;
}
//添加接口
接口FooBar
{
Foo getFoo();
Bar getBar();
}
班车
//添加的工具
实现FooBar
{
私有Foo-Foo=新Foo();
私人酒吧=新酒吧();
公共对象获取(最终字符串名称)
{
if(name.equals(“foo”))
{
返回(foo);
}
如果(名称等于(“bar”))
{
返回(巴);
}
抛出新错误();
}
//附加方法
公共Foo getFoo()
{
返回((Foo)get(“Foo”));
}
//附加方法
公共酒吧
{
返回((Bar)获取(“Bar”);
}
}
//不变的
福班
{
}
//不变的
分类栏
{
}

因为您提到您有修改类层次结构的自由

  • 将基MyClass ctor更改为接受3个参数params、foo和bar。注释掉GlobalVar引用并缓存传入的值
  • 编译。。这将引发一系列编译错误-没有接受1个参数的ctor
  • 将每一个都修复为传入GlobalVar.get(“foo”)和GlobalVar.get(“bar”)。让它建造起来
  • 优化:现在通过延迟加载和缓存foo和bar值来最小化对DB的点击。通过GlobalVar上的某些属性公开

您是否也可以完全控制子类(这意味着您可以对所有子类进行更改)?是的,所有子类都可以修改。
this.params = Collections.unmodifiableMap(params);
import java.util.Hashtable;


class MyClass
{
    private final Foo foo;
    private final Bar bar;
    private final Hashtable<String, String> params;

    public MyClass(final Hashtable<String, String> params)
    {
        this(params, GlobalClass.GLOBALVAR);
    }

    // added constructor
    public MyClass(final Hashtable<String, String> params, 
                   final FooBar fooBar)
    {
        this.foo    = fooBar.getFoo();
        this.bar    = fooBar.getBar();
        this.params = params;
    }
}

class MySubClass
    extends MyClass
{
    public MySubClass(final Hashtable<String, String> params)
    {
        super(params);
    }

    // added constructor
    public MySubClass(final Hashtable<String, String> params, 
                      final FooBar fooBar)
    {
        super(params, fooBar);
    }
}

// unchanged
class GlobalClass
{
    public static Car GLOBALVAR;
}

// added interface
interface FooBar
{
    Foo getFoo();
    Bar getBar();
}

class Car
    // added implements
    implements FooBar
{
    private Foo foo = new Foo();
    private Bar bar = new Bar();

    public Object get(final String name)
    {
        if(name.equals("foo"))
        {
            return (foo);
        }

        if(name.equals("bar"))
        {
            return (bar);
        }

        throw new Error();
    }

    // added method
    public Foo getFoo()
    {
        return ((Foo)get("foo"));
    }

    // added method
    public Bar getBar()
    {
        return ((Bar)get("bar"));
    }
}

// unchanged
class Foo
{
}

// unchanged
class Bar
{
}