【问题标题】:How can I safely convert a static class to an instantiated class in C#如何安全地将静态类转换为 C# 中的实例化类
【发布时间】:2013-07-18 15:34:50
【问题描述】:

我最近(几个月前)换了工作,并继承了一个代码库,该代码库尽可能多次违反 SOLID 原则。似乎编写此代码的人决定详细研究每一个良好的编码实践,并尽可能频繁、最彻底地违反它们。

我是该产品的唯一开发人员 - 组织中没有人知道代码,而且代码库太大而复杂,无法完全重写。我正在研究我可以做出的最高价值的改变,以使代码库变得灵活和健壮。放弃这个产品也不是一种选择。

产品中所有问题的根源都源于一组核心业务逻辑数据结构的类。这些类有很多问题,但我真正感兴趣的是以下几点:

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

为了简洁起见,我从这个类中剪掉了很多其他方法。

如您所见,围绕这个类存在大量问题(在静态类中存储状态是一种巨大的代码异味 - 围绕该类编写整个应用程序简直太疯狂了)。

我目前的意图是将这个类重构为一个适当的单例类(并最终成为一个常规类,以便我可以让用户同时打开多个文档)。

我应该这样做吗?

进行此更改的最大风险是什么?我可以采取任何方法来降低进行此更改的风险吗?

【问题讨论】:

  • 从整个类中删除 static 关键字并重建项目。你会得到错误。修复这些,它是安全的。
  • 我没有任何提示,但我忍不住注意到你的头像和你的问题一样。
  • 我的默认头像是我的头像,与本站无关。所以我把我的头像改成了我的想法,就是这段代码。

标签: c# .net architecture


【解决方案1】:

是的,转换为单例似乎是很好的第一步。它仍然不是线程安全的,但它是一个开始。然后,您可以将其从 true 单例更改为 允许 构造单独实例的单例,但也具有与单例相同的“默认”实例。 (当然,您可以将“实例持有者”分离到一个单独的类中。)这将允许您开始编写每次都以新实例开头的可测试代码。

之后,您可以开始引入依赖注入,以便每个需要访问同一个实例的类都能获得它......并删除“默认”实例。

当然,如果您可以减少需要访问同一实例的类的数量,那就更好了。

对于线程,您要么需要锁定每个方法,要么使用ConcurrentDictionary

【讨论】:

  • 这与我将要采取的方法一致。感谢您关于线程的提醒。多线程目前还不可能(由于继承代码和第三方非线程安全组件的组合),但我想在某个时候添加并发性,因为那里有一些繁重的、可并行化的 CPU 工作。
【解决方案2】:

如果您对这种类型在您的应用程序中的流动方式一无所知,那么这是一项危险的任务。但是,如果您真的需要这样做而不破坏一切,我愿意:

知道我需要的是文档之间的不同划分,并且我知道(已被时间证明)这种类型对单个文档有效,让我们添加文档切片。

假设Document 具有Name 属性,我们可以考虑类似(示例):

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

使所有字段静态:

   //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;

将它们移到内部

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;
       ....

  }

因此,您可以在单个文档和与它们相关的数据之间创建具体的隔离。

在主类里面你可以有:

public static class NetCollection
{
    ... 

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

这只是一个大概的想法(草图),您肯定需要对其进行更改以适应您的需要

【讨论】:

  • 我真的很喜欢这个解决方案。如果我理解正确,我可以使用当前的静态类作为内部实例类的有效包装器,因此不会破坏现有代码,同时允许可测试性和扩展。
  • 保留(尽可能)公共方法,因此外部世界的方法可以按原样在您的类型内部看到。这将帮助您不更改所有调用者代码,因此可能会在类 internal 实现上造成混乱和无花果。我再说一遍,这是一个指南,要走的路,但你会遇到问题(可以保留所有公共方法,某些类型的可见性问题,100+ 其他东西..),所以请继续一个简单的想法:不要触摸其他人可见的内容并更改类型的 内部 行为。所以所有可用的调用者将继续以相同的方式调用它,就像以前一样。
【解决方案3】:

你能把它包裹在一个静态工厂类中吗?把你认为需要的所有东西都做成单例,并根据需要保持某些东西不变?它会解决一些问题并为您提供一些灵活性。

【讨论】:

【解决方案4】:

static 类和 Singleton 之间没有真正的区别。是的,它的实现方式不同,但你有同样的问题(只是为了不拥有-static我认为没有必要采取行动)。

如果您能够获得“真实”实例,那就去做吧。但是仅仅因为 Lint 不抱怨而重构为 Singleton 并不是要走的路,恕我直言。

【讨论】:

  • 在我看来,我首先考虑采用单例路径的原因是它对所有相关代码的影响最小。必须直接管理实例将是一场噩梦。
猜你喜欢
  • 1970-01-01
  • 2015-04-23
  • 2011-08-20
  • 1970-01-01
  • 2021-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多