How to refactor “stringly-typed” code?

2019-04-08 07:24发布

I'm currently working on a codebase where there are a few classes of variable, like database paths, which are simply represented as Strings. Most of the operations on these (non-)types are defined in a utility class.

I have created a new class to represent a database, with operations defined as instance methods, in traditional OOP style. However it is quite laborious to go through the large codebase and refactor it to use the new types. Does anyone have any advice as to how to do this quickly and effectively?

4条回答
SAY GOODBYE
2楼-- · 2019-04-08 07:47

A technique I've used in C# (and just ported to Java - apologies if I've made an error, I'm new to Java) is to create StringlyTyped classes, e.g. a base class

public abstract class StringlyTyped {
    private String value;

    public StringlyTyped (String value){

        if (value == null){
            throw new IllegalArgumentException("value must not be null");
        }
        this.value = value;
    }

    public String getValue() { return value; }

    @Override
    public boolean equals(Object other){
        if (other == this) {
            return true;
        }

        if (other == null || !this.getClass().equals(other.getClass())){
            return false;
        }

        StringlyTyped o = (StringlyTyped)other;
        return o.getValue().equals(this.getValue());
    }

    @Override
    public int hashCode(){ return this.getValue().hashCode(); }

    @Override
    public String toString() { return this.getValue();  }
}

Then derived class

public class ProviderName extends  StringlyTyped {

    public ProviderName(String value) {
        super(value);
    }
}

And usage

public void Foo(ProviderName provider) { 
}

It makes sense when you have methods with many String parametrers, e.g. you can avoid

public void Foo(String username, String password, String db, String table, String constraint) 

and instead have code that is strongly typed like this:

public void Foo(UserName username, Password password, DatabasePath db, TableName table...) 

etc...

查看更多
Bombasti
3楼-- · 2019-04-08 07:48

Database paths sound like they should be Strings to me. What else makes sense? And they should be externalized, either in configuration files or a database. That's the least of your problems.

Persistence has been so many times over (e.g. Hibernate, Spring JDBC, iBatis, etc.) that I'd wonder how you could possibly improve on them. If you have to go to the trouble of refactoring - and you must - I'd advise using anything other than what you've done.

If you must write something, Google for "generic DAO". You'll get stuff like this:

http://www.ibm.com/developerworks/java/library/j-genericdao/index.html

If your work isn't patterned after something like that, throw it away and re-think things.

查看更多
虎瘦雄心在
4楼-- · 2019-04-08 08:03

I generally try to isolate the strings at the limit of the application/process boundary, such as when they are retrieved from a database or received via a web operation.

At that application/process boundary is often the ideal place to map/convert/deserialize the strings into a more proper object model, as supported by whatever language you are using.

Similarly, the object model can be mapped/converted/serialized back into string form as it exits your application/process boundary.

It is worth noting that this stringly typing can be somewhat subtle. I commonly see xml intruding into application and domain layers. A similar example from the .NET space would be failing to map ADO.NET DataTables (with their string column names and untyped field values) into classes/objects pretty much as soon as they are received. I have no doubt that there are many similar equivalents in the Java world. Stringly Typing is not just limited to date values, as the joke goes.

查看更多
叛逆
5楼-- · 2019-04-08 08:04

Migrate the utility class to use your new class. Then the utility class methods should only contain two statements. One for creating your class and the other is invoking your class. After that, you can inline the utility class methods thereby eliminating the need for it.

When you are finished with that, you need to look for a way to not instantiate your new class over and over again. This should be done by refactoring the local variable to an instance field which is initialized at construction time.

查看更多
登录 后发表回答