Static Substitution Static Replacement (Translation)

xiaoxiao2021-03-06  45

Static Substitution Static Replacement (Translation)

Martin Fowler

Original: http://martinfowler.com/bliki/staticsubstitude.html

When our development group discusses their work, a topic that is often heard is that they don't like to use things in static (STATIC) variables. We usually see that some Services or Components are saved in static variables in static initialization code blocks. One of the largest issues of using static variables (most languages ​​have this feature) is not possible to replace a specific implementation (IMPLEMENTATION) to another. This will cause a lot of constraints to us - for us to test (drive development) super fans - use Service Stub instead of service, is very important for testing well.

Here is an example of this static variable.

[code]

Public class addressbook {

Private Static String Connectionstring, UserName, Password;

STATIC {

Properties PROPS = getProperties ();

Connectionstring = (string) Props.get ("db.connectionstring");

Password = (string) Props.get ("db.password");

Username = (string) Props.get ("db.username");

}

Public Static Person FindbyLastName (String s) {

String query = "SELECT LASTNAME, FIRSTNAME AUNTNAME =?";

Connection 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);

}

}

[/ code]

In the example, we have some configuration values ​​initialized in the static initialization code block, and a static method that executes queries on the database.

Using this way, it is easy to achieve some changes to the program. For example, by changing the Properties file, we can easily replace the database used by the program. But for the test, we may not use the database at all - using a simple return of the Packaged data.

In order to allow this simple replacement, we need to do some simple reconstruction. The first step is to convert static variables to a single example (Singleton).

[code]

Public class addressbook {

Private static addressbook soleinstance = new addressbook (); private string connectionstring, username, password;

Public addressbook () {

Properties PROPS = getProperties ();

Connectionstring = (string) Props.get ("db.connectionstring");

Password = (string) Props.get ("db.password");

Username = (string) Props.get ("db.username");

}

Public Static Person FindbyLastName (String s) {

Return soleinstance.findbylastnameImpl (s);

}

Public person FindbyLastNameIMPL (String s) {

String query = "SELECT LASTNAME, FIRSTNAME AUNTNAME =?";

Connection 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);

}

}

[/ code]

This is a fairly simple and clear reconstruction.

Let's turn the static data in the old class into instance data. The static initialization code is then moved to the constructor. Finally, we move all public methods to an example, just simply call the instance method in the original static method.

There is no way in the reconstructed classification - maybe we should call it "Replace Statics with Singleton). Shouldn't this, this reconstruction does not change any of the functions of the original program, but it has made the program a step in supporting the ability to support the replacement. The next step is to introduce a method of loading a separate instance.

[code]

Public Static Void LoadInstance (AddressBook Arg) {

Soleinstance = arg;

}

[/ code]

Now, we are ready to replace the test (or other purpose). In a test case, we can add a suitable method call to the SETUP method of the test: AddressBook.LoadInstance (New StubaddressBook ());

Just write a subclass inherited from AddressBook, we can use it instead of actual things (such as database) testing.

Things did not end this. In the above code, even if it is not used, we will create an instance of a service - because a separate instance is initialized in a static initialization code. This makes the code of service use and created dependencies, and the flexibility of the code is reduced. To solve this problem, we need to move such a static initialization code to a replaceable initialization class. (You can refer to Chris for more information on this). But at least, what we do is a very useful step to deal with such problems. This reconstruction also brings problems that can be used in single case mode. It is important to note that if you use a single example (or other form of registry), it is easy to replace these single examples and their instantiated methods.

I just got the new book of Michael Feathers Working Effectively With Legacy Code, he talked more (and better) to deal with similar problems in the book.

转载请注明原文地址:https://www.9cbs.com/read-79191.html

New Post(0)