atticsubstitutit

2004年10月20日

正如我倾听我们的开发团队谈论他们的工作,一个共同的主题是他们对估计患者的东西不喜欢。通常,我们看到具有静态初始化器的静态变量中的公共服务或组件。静态的大问题之一(大多数语言)是您不能使用多态性来用另一个实现一个实现。这让我们很多,因为我们是测试的伟大粉丝 - 并测试嗯,能够用a取代服务很重要服务存根

这是这种静态的一个例子。

公共类地址簿{私有静态字符串ConnectionString,用户名,密码;静态{属性props = getProperties();connectionstring =(字符串)props.get(“db.connectionstring”);密码=(字符串)props.get(“db.password”);UserName =(String)props.get(“db.username”);公共静态人findbylastname(string s){string query =“select lastname,firstname来自lastname =的人=?”;连接conn = null;PreparedStatement st = null;ResultSet RS = null;尝试{conn = drivermanager.getConnection(ConnectionString,用户名,密码); st = conn.prepareStatement(query); st.setString(1, s); rs = st.executeQuery(); rs.next(); Person result = new Person (rs.getString(2), rs.getString(1)); return result; } catch (Exception e) { throw new RuntimeException(e); } finally { cleanUp(conn, st, rs); } }

因此,我们这里有一堆在静态初始化程序中初始化的一堆配置内容,然后是对数据库运行查询的静态方法。

有些变化很容易。通过更改属性文件,我们可以轻松更改该程序运行的数据库。但是对于测试,我们可能不希望在数据库中运行它 - 一个简单的存根只会返回罐头数据。

为了允许简单的替换,我们需要做一点重构。188app彩票ios第一步是将静态分化为单身。

公共类地址簿{私人静态地址簿索泰宜家=新地址簿();私有字符串ConnectionString,用户名,密码;公共地址簿(){属性props = getProperties();connectionstring =(字符串)props.get(“db.connectionstring”);密码=(字符串)props.get(“db.password”);UserName =(String)props.get(“db.username”);公共静态人FindBylastname(String s){return soleinstance.findbylastnameimpl(s);公共人员findbylastnameimpl(string s){string query =“选择lastname,来自lastname =的人的名字=?”;连接conn = null;PreparedStatement st = null; ResultSet rs = null; try { conn = DriverManager.getConnection(connectionString, username, password); st = conn.prepareStatement(query); st.setString(1, s); rs = st.executeQuery(); rs.next(); Person result = new Person (rs.getString(2), rs.getString(1)); return result; } catch (Exception e) { throw new RuntimeException(e); } finally { cleanUp(conn, st, rs); } }

这是一个非常简单的重构。188app彩票ios

  • 我们在旧类上占据所有静态数据,然后将其转化为实例数据。
  • 我们移动静态初始化代码并将其移动到构造函数中。
  • 我们采用所有公共方法,将它们的主体移动到实例上,将静态方法作为一个简单的替代器。

我没有在目录中重构 - 也许我应该称188app彩票ios之为用单例替换静态。由于它的代价不会改变任何东西,但这是支持替代的一步。下一步是介绍一个加载唯一实例的方法。

公共静态void loadInstance(AddressBook arg){索泰斯坦= arg;}

这现在准备我们替换测试(或其他)目的。现在在一个测试用例中,我们可以在测试中添加合适的呼叫设置方法:addressbook.loadStance(new stupaddressbook());。只要存根子类化簿,我们现在就可以针对一个存根而不是真实的东西。

这不是故事的结尾。特别是使用此代码,即使我们从未使用它,我们必须创建实际服务的实例 - 因为唯一的实例是在静态初始化程序中初始化的。这迫使依赖于服务访问代码,这可能导致自己的疼痛。要处理此问题,我们需要从静态初始化器中移动任何此类初始化,并进入本身可替代的单独初始化程序类。(看克里斯有关此信息。)但至少这提供了有用的第一步。

这也提出了Singlettons可以存储的一些问题。特别是如果你使用单例(或其他形式的注册表)确保它们可以很容易地替换,并且还可以轻松更换它们的初始化。

我刚收到了迈克尔羽毛的新书的副本用遗留代码有效地工作。他谈论更多(更好)关于许多这些问题。