又一个“众所周知”的DAL层设计BUG

时间:2022-10-01 21:27:34

      DAL层使用抽象工厂是大家再熟悉不过的设计方法了。最近在回顾项目的时候,发现网上流传的方法大多都存在一个不大不小BUG。对于整个系统而言,轻则需要重新更新配置,重则需要重启。

      好了废话不多说,先看代码

        private static void GetProvider()
        {
            try
            {
                _instance = (IDataProvider)Activator.CreateInstance(Type.GetType(string.Format("xxxx", ConnTypeString), false, true));
            }
            catch
            {
                _instance = null;
                throw new Exception();
            }
        }

        public static IDataProvider GetInstance()
        {
            if (_instance == null)
            {
                lock (_lockHelper)
                {
                    if (_instance == null)
                    {
                        GetProvider();
                    }
                }
            }
            return _instance;
        }

 

      我相信绝大多数的抽象工厂都是这样写的。因为设计模式的书上也是这样写的,网上能够找到的例子也是这样写的,许多大型项目(譬如DZNT)也是这样写的。

      我为什么说这样的设计存在BUG呢?

      这还要回到我们使用反射+配置文件+抽象工厂的DAL层的设计初衷。我们不希望把DAL使用何种数据库写死在代码中,而是希望灵活的切换数据库。回头看看代码,似乎没有发现什么问题。再细想“灵活切换数据”,在系统正在运行的时候切换数据库可以吗?答案是不可以。因为系统启动之后instance已经被初始化了,当你再次修改配置信息的时候就不会再次反射出新的instance。所以这里存在这样一个BUG。

      修改之后的代码:

        public static IDataProvider GetInstance()
        {
            string connType = Configs.GetConfigs().DbConfigInfo.ConnType;
            if (_currentConnType != connType || _instance == null)
            {
                lock (_lockHelper)
                {
                    if (_currentConnType != connType || _instance == null)
                    {
                        _currentConnType = connType;
                        GetProvider();
                    }
                }
            }
            return _instance;
        }

      你在看完以后可以有种种理由反驳我

  1. “我在修改配置文件的时候直接重置instance就可以了”
  2. “我的程序只需要在启动的时候判断使用什么数据库就足够了,在运行时不会切换数据库”
  3. “切换数据库必须保证数据安全,需要关闭系统,重新备份数据库再启动系统”

 

      我并没有充足的理由回应你的驳斥。后两个观点更有理由让这个BUG存在。的确,在系统中我们没有必要考虑到每一个细节,只要能够让系统按照需求运行正常即可。所以我只想针对第一点说说我的看法。首先DZNT(老版本,新版代码未阅读)就是这样做的,提供了一个Reset重置instance。但这样设计似乎违背了“单一职责原则”,“一个类,最好只做一件事,只有一个引起它变化的原因”。所以在修改配置的时候就不应该去管DAL的事情,DAL的事情应该由DAL自己解决。同时也避免了在配置完数据库类型之后忘记Reset引起的BUG。