设计模式-控制反转

写了几年代码后,一般都会接触到设计模式这个东西。它其实是一种编程思想,与具体什么语言没多大关系。

先看看网上大概的设计模式科普文章https://www.cnblogs.com/abcdwxc/archive/2007/10/30/942834.html。  一般都是很枯燥的为了讲技术去实现一个技术。

我看了反正不知所云,心里只有一个问号,代码有必要这样吗?初学的人容易走火入魔,写出来的代码舍本逐末。

 我一个两三百行代码的简单小程序,非要实现这啊那啊设计模式,似乎也没有实际意义,没必要为了技术而技术

 

医药上有句话叫 抛开剂量谈毒性,都是耍流氓。 我们抛开问题来谈设计模式,也显得很多余。

我觉得应该要结合编码中遇到的实际问题,以及如何用设计模式去解决这些问题来探讨

 

先来看一段代码,我有一个实现保存用户设置的类 UserInfoHelpJson,MainFun类里则会使用UserInfoHelpJson去完成保存用户设置的操作

    public class UserInfoHelpJson
    {
        public void SaveUserData()
        {
            //......具体操作略
            Console.WriteLine("把用户设置信息以Json格式写到文件");
        }
        public void LoadUserData()
        {
            //......具体操作略
            Console.WriteLine("从Json格式文件读取用户设置信息");
        }
    }


    public class MainFun
    {
        public void doSomeOper()
        {
            UserInfoHelpJson userInfoHelp = new UserInfoHelpJson();//代码这样写有什么问题吗??
            userInfoHelp.SaveUserData();
        }
    }
    

 

 UserInfoHelpJson userInfoHelp = new UserInfoHelpJson(); 这么写到底有没有问题?

一定会有人说这样Mainfun类就对UserInfoHelpJson类产生了强依赖啊啥的,总之就是说这样写不够高级,嫌弃它。  

但我觉得如果我的程序就只是我一个人开发,也只需要支持把userinfo 写入到json文件这一种模式,这么写是没有任何问题的。好的很!!!  

 

场景1 那么如果不止MainFun里要用到  UserInfoHelpJson ,还有其他很多地方MainFun1,MainFun2......里也用到了UserInfoHelpJson 。

有一天领导和我说,客户需要增加一种用户设置保存到xml格式的文件的方式,支持让他们自己配置是保存为xml ,还是保存为json

于是我加了一个xml的实现类。我难过的发现有一百处调用,我就得改100个地方。

    public class UserInfoHelpXml
    {
        public void SaveUserData()
        {
            //......具体操作略
            Console.WriteLine("把用户设置信息以Xml格式写到文件");
        }
        public void LoadUserData()
        {
            //......具体操作略
            Console.WriteLine("从Xml格式文件读取用户设置信息");
        }
    }


...调用的地方,改程如下。伪代码
  if(保存方式==xml)
     userinfohelp= new UserInfoHelpXml();
  else if(保存方式==Json)
     userinfohelp= new UserInfoHelpJson();

  userinfohelp.SaveUserData();
 
 

 

那么有没有写法,可以让我不用修改这么多地方那? 必然是有的,我们只需要定义一个接口,让 xml和json 都来实现这个接口。每个调用的地方使用接口即可

    public interface IUserInfoHelp
    {
         void SaveUserData();
        void LoadUserData();
    }
    public class UserInfoHelpJson: IUserInfoHelp
    {
        public void SaveUserData()
        {
            //......具体操作略
            Console.WriteLine("把用户设置信息以Json格式写到文件");
        }
        public void LoadUserData()
        {
            //......具体操作略
            Console.WriteLine("从Json格式文件读取用户设置信息");
        }
    }
    public class UserInfoHelpXml: IUserInfoHelp
    {
        public void SaveUserData()
        {
            //......具体操作略
            Console.WriteLine("把用户设置信息以Xml格式写到文件");
        }
        public void LoadUserData()
        {
            //......具体操作略
            Console.WriteLine("从Xml格式文件读取用户设置信息");
        }
    }
    public class Factory
    {
        static string SaveModel = "";//这个字段可以从app.xml配置文件中加载,或在程序里动界面上设置等不同方式实现
        public static IUserInfoHelp UserInfoHelp()
        {
            if (SaveModel == "xml")
                return new UserInfoHelpJson();
            else if (SaveModel == "json")
                return new UserInfoHelpJson();

            return null;
        }
    }
    public class MainFun
    {
        public void doSomeOper()
        {
            IUserInfoHelp userInfoHelp = Factory.UserInfoHelp();   //使用接口的好处,无论有多少次调用,这里都不需要再修改
            userInfoHelp.SaveUserData();
        }
    }

 

看上面的工厂类,其实他叫啥名字并不重要,考虑到设计模式里面,首先就讲到了工厂模式,我们就叫它Factory。

里面使用了接口来返回对象 , 接口和虚函数一样,实现了函数调用的多态。如果没有接口/虚函数。工厂不工厂的其实没有什么意义

有了它。就算我后期想把数据保存到SQL server,MySQL,sqlite等等,新建了 UserInfoHelpSqlserver , UserInfoHelpMysql, UserInfoHelpSqlite   等等等等,也只需要改这么一个地方就可以啦。

 

场景2  

随着我的程序越来越复杂,我一个人慢慢写不过来了。所以老板又招了几来个人参与项目。大家都开发一部分功能。作为负责人,我把程序按功能划分分成了几个部分,独立出各个dll。

 

某天我给新来的一个员工小张安排,让他负责开发新的 UserInfoHelpOracle  把用户数据都保存到oracle数据库里,我难过的发现,我必须等他开发完,把DLL提供给我以后,main.exe 里 才能新加上一句 else if( saveModel==“Oracle”) 。。。才能编译通过

 

 

 我明明今天就有空写代码,明后天我想请假了,但是由于小张没有完成他的dll,导致我今天没法写(至少小张要完成一个带空壳函数的dll给我)。   

我想了想,这个比较也好办,把这个Factory 类交给小张来写把,事实上很多项目很多公司也是这么干的,以后来了小王,小李,让他们也干完后再来改下Factory 类。行吧? 可以的,问题不大

 

但是小张改完他的dll后,我的main.exe 也必须重新编译。 这个就比较恶心了,如果我的main.exe 是个服务器程序,比如是一个 土者荣耀的游戏的服务器程序,那小张改了以后,程序就得编译,替换,再重新启动啊。

更多的时候,我希望我的主程序框架啥也不用改,谁的dll开发完,拿过来,最多我改改配置文件就能直接用上就好了,就是网上挺流行的那个【插件式开发】,那样就行。

 

这个问题的本质在于,通过new 创建对象,必然有一个强依赖,那么还有没有别的方式创建对象那? C# 里面,我们还可以用反射,于是我改一下我的Factory类

    public class Factory
    {
        //以下字段可以从app.xml配置文件中加载,或在程序里动界面上设置等不同方式实现
        public static string dllname , classname ;
        public static IUserInfoHelp UserInfoHelp()
        {
            dllname= System.Configuration.ConfigurationSettings.AppSettings["dllname"];
            classname= System.Configuration.ConfigurationSettings.AppSettings["classname"];
            var assemb = Assembly.LoadFrom(dllname);
            var obj = assemb.CreateInstance(classname);
            return (IUserInfoHelp)obj;
        }
    }

 

 

 

这样,不管我叫小王,小李,小红还是谁,开发了一个dll,只要遵循 IUserInfoHelp接口,拿过来,我再改下我的app.config配置文件,就能直接用上了。

这便是插件式开发了把

 

写到这里,已经有一些IOC 控制反转  -DI 依赖注入的雏形了,如果用过prism/light 这类框架,会发现好像里面通过容器Container返回对象,还有点像我们的Factory的功能

应该说这样的编程思想和需求我们已经有了

下一篇再探讨下 IOC容器的常见框架和自己如何实现把

 

posted on 2021-08-13 16:35  陈傻傻周笨笨  阅读(87)  评论(0编辑  收藏  举报

导航