zoukankan      html  css  js  c++  java
  • 设计模式-控制反转

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

    先看看网上大概的设计模式科普文章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容器的常见框架和自己如何实现把

  • 相关阅读:
    window.location.Reload()和window.location.href 区别
    PHP substr(),mb_substr()及mb_strcut的区别和用法
    jstree节点展开设置
    关于Jquery中ajax方法data参数用法
    HTML相对路径(Relative Path)和绝对路径(Absolute Path)
    Win32基础编程了解窗口类
    Visual C++ ActiveX 开发指南:第一章 什么是ActiveX
    分粥
    蛙蛙推荐:ASP实现自定义标签模板
    蛙蛙请教:如何利用委托实现多个方法同时调用.
  • 原文地址:https://www.cnblogs.com/CSSZBB/p/15127241.html
Copyright © 2011-2022 走看看