简体   繁体   中英

How can I safely convert a static class to an instantiated class in C#

I have recently (a couple of months ago) changed jobs and have inherited a codebase which violates every single one of the SOLID principles as many times as it possibly can. It seems as though the people who wrote this code decided to study every single good coding practice in detail and violate them all as often and most radically as they possibly could.

I am the sole developer of this product - there is no one left in the organisation who knows the code and the codebase is too large and complex to completely rewrite. I am looking at the highest value changes that I can make to make the codebase flexible and robust. It is not an option to abandon this product either.

The root of all problems in the product stems from a group of classes which are the core business logic data structures. There is a lot of problems with these classes, but what I am really interested in is the following:

public static class NetCollection
{
    private static Logger LogFile { get { return Logger.GetMethodLogger(2); } }
    // Declare local variables.
    private static Dictionary<string, NetObject> netObjectHashTable;
    private static Dictionary<string, NetTitle> titlePropertyHashTable;
    private static Dictionary<string, NetObject> referenceDataHashTable;
    private static Dictionary<int, SortedDictionary<string, int>> picklistHashTable;

    public static IEnumerable<NetObject> NetObjects
    {
        get
        {
            return netObjectHashTable.Values;
        }
    }

    static NetCollection()
    {
        netObjectHashTable = new Dictionary<string, NetObject>();
        titlePropertyHashTable = new Dictionary<string, NetTitle>();
        referenceDataHashTable = new Dictionary<string, NetObject>();
        picklistHashTable = new Dictionary<int, SortedDictionary<string, int>>();
    }

    public static void AddNetObject(NetObject newObject)
    {
        if (newObject == null)
            return;
        if (newObject.TitleType == "Reference Data")
        {
            // Check if hash table contains key
            if (!referenceDataHashTable.ContainsKey(newObject.ID.ToString()))
            {
                referenceDataHashTable.Add(newObject.ID.ToString(), newObject);
            }
        }
        else
        {
            // Check if hash table contains key
            if (!netObjectHashTable.ContainsKey(newObject.ID.ToString()))
            {
                netObjectHashTable.Add(newObject.ID.ToString(), newObject);
            }
        }
    }
}

I have snipped quite a number of other methods from this class for the sake of brevity.

As you can see, there are a huge number of issues around this class (storing state in a static class is a huge code smell - writing your entire application around said class is just crazy).

My current intention is to refactor this class into a proper singleton class (and eventually into a regular class so that I can enable the user to open multiple documents simultaneously).

Should I do this?

What are the biggest risks with making this change? Are there any approaches that I can take to mitigate the risk of making this change?

Yes, converting to a singleton seems like a good first step. It still won't be thread-safe, but it's a start. You can then change it from being a true singleton to one which allows separate instances to be constructed, but also has a "default" instance in the same way as a singleton. (You could separate the "instance holder" into a separate class, of course.) That will allow you to start writing testable code which starts with a fresh instance each time.

After that, you can start introducing dependency injection so that each class that needs access to the same instance gets it... and remove the "default" instance.

Of course if you can reduce the number of classes which need access to the same instance, that would be better too.

For threading, you'll either need to lock in each method, or use ConcurrentDictionary .

If you don't know anything about how this type flows in your application, it's a dangerous task. But if you really need to do that without, possibly, breaking everything, I would do like:

Knowing that what I need is a distinct devision between documents, and I know (it's proved by time) that this type works for a single document, let's add Document slice.

Let's assume Document has Name property, we can think about something like (example):

public static void AddNetObject(string documentName, NetObject newObject)
{
    ....
}

make all fields non static:

   //NO STATIC
    ...
    private Logger LogFile { get { return Logger.GetMethodLogger(2); } }   
    private Dictionary<string, NetObject> netObjectHashTable;
    private Dictionary<string, NetTitle> titlePropertyHashTable;
    private Dictionary<string, NetObject> referenceDataHashTable;
    private Dictionary<int, SortedDictionary<string, int>> picklistHashTable;

Move them into internal

private class NetDocument {
       public string DocumentName {get;set;} //DEFINE DOCUMENT IT RELATED TO !
       ...
       private Logger LogFile { get { return Logger.GetMethodLogger(2); } }   
       private Dictionary<string, NetObject> netObjectHashTable;
       ....

  }

So you create concrete isolation between single documents and related to them data.

After inside the main class yo can have:

public static class NetCollection
{
    ... 

    //Key: document name
    //Value: NetDocument data
    private Dictionary<string, NetDocument> documents = new ....
}

This is just a general idea (a sketch) , you for sure need to change it to fit your needs .

Could you wrap this around a static Factory class? make everything that you think you need as a singleton, and keep certain things static as needed? It'll solve some of the problems and leave you with some flexibility.

There is no real difference between a static class and a Singleton. Yes, it is implemented differently, but you have the same problems (and just for the sake of not-having- static I don't think it is neccessary to act).

If you're able to go for a "real" instances, do it. But just refactoring to Singleton because Lint doesn't complain is not the way to go IMHO.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM