面向对象设计原则详解:单一职责原则

 定义:就一个类而言,应该仅有一个引起它变化的原因。

  就是说一个类只做一件事,只关注它必须关注的,用哲学的语言来说,就是只抓主要矛盾。如果一个类承担了太多的职责,那就意味着这个类和其它程序集,或其它类总是互相影响,会造成整个程序逻辑混乱,既不利于程序的健壮性,也不利于后期维护。所以在设计之前,就要规划好各个类的,各个程序集的职责,显示界面的就只关心界面显示,不要再显示界面的类里去处理后台要做的事;读写数据库的类,就只需要负责把数据读出来和将数据保存进去,而不应该还要去考虑界面如何显示。控制层就只管控制,当前需要那些操作。那么这些过程中的复杂操作,算法呢?那就再新建类吧。反正就遵循一点,这个类只做一件事,并且只有一个地方能影响它。

  这样做有什么好处呢?我觉得最主要有以下两点。

  1、细化任务,思路更清晰。一个稍微大的项目,编码量很大,数千行,甚至数万行。如果不遵守单一职责原则,新建类的时候不按职责划分,将会造成,类和类之间,没有条理性差,不光自己写代码很费劲,一旦有人中途接手或维护,会让别人花费很大的精力熟悉你的流程。而且修改代码,还会造成连锁反应,旧bug去了,新bug又来了。那么将类按照职责分的详细的话,流程就很清晰,读代码也更顺利。

比如说要完成下面的需求,

一个text文件:

诺基亚    =N8

摩托罗拉  =ME525+ 

华为   =HONOR

HTC=A3366/T9299

小米=M1

按钮触发一个事件,将文件中<key,value>读取出来,显示在richtextbox中。效果如图。

  有人可能会这样写,比如我刚开始写代码的时候就是这样的思维,所有的操作都在Form里:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            string filePath = Application.StartupPath + @"\configure\test.txt";

            Dictionary<string, string> contentDictionary = new Dictionary<string, string>();

             if (!File.Exists(filePath))
            {
                return ;
            }
            FileStream fileStream = null;

            StreamReader streamReader = null;

             try
            {
                fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);

                streamReader = new StreamReader(fileStream, Encoding.Default);

                fileStream.Seek(0, SeekOrigin.Begin);

                string content = streamReader.ReadLine();

                while (content != null)
                {
                    if (content.Contains("="))
                    {
                        string key = content.Substring(0, content.LastIndexOf("=")).Trim();

                        string value = content.Substring(content.LastIndexOf("=") + 1).Trim();

                        if (!contentDictionary.ContainsKey(key))
                        {
                            contentDictionary.Add(key, value);
                        }
                    }
                    content = streamReader.ReadLine();
                }
            }
            catch
            {

            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                }

                if (streamReader != null)
                {
                    streamReader.Close();

                }
            }
            string rctBoxText = "读取的内容为:" + System.Environment.NewLine;

             foreach (string key in contentDictionary.Keys)
            {
                rctBoxText += key + @"=" + contentDictionary[key] + System.Environment.NewLine;
            }
             this.richTextBox1.Text = rctBoxText;
        } 

        //public static Dictionary<string, string> ReadLineFile()
        //{
        //}
    }

  最多超常发挥一下,把读取的那个部分写一个方法。那么我们来看,这样界面担负了多少职责:显示,读取,维护文件路径。如果那个读取的文件放到了别的地方,就要打开界面的代码修改。而且读取的部分要引用IO程序集,这也不合理,这是一个界面和IO有什么关系呢,界面就应该关注显示的一系列就可以了。那么我们修改一下。按职责分开。

Common类,负责维护路径,以及其他公共的变量或方法。

    public class Common
    {
        public static string StartupPath
        {
            get
            {
               return Application.StartupPath + @"\configure\";
            }
        }
    }

ReadFileManager类,只负责读取,至于过程主界面不关心,主界面最终只需要结果。这样即使ReadFileManager类发生了错误,也不会导致程序的崩溃,反映在界面上的只是结果的错误。

界面呢,只负责显示,

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        } 

        private void button1_Click(object sender, EventArgs e)
        {
            Dictionary<string, string> contentDictionary = ReadFileManager.ReadLineFile();

            string rctBoxText = "读取的内容为:" + System.Environment.NewLine;

            foreach (string key in contentDictionary.Keys)
            {
                rctBoxText += key + @"=" + contentDictionary[key] + System.Environment.NewLine;
            } 
            this.richTextBox1.Text = rctBoxText;
        }
    }

  这只是一个简单的需求,复杂的程序的话,比如主界面控件比较多,那么就需要再建一个类FormManager,它的职责是管理主界面。像上面的代码,主界面里不应该直接调用ReadFileManager.ReadLineFile(),而是直接交给FormManager完成。

  2、任务单一,提高了程序的健壮性。这个比较好理解,一个类只完成一件事,即时操作失败,影响的面也会很小。比如像上面的修改之前的方式,如果在读取过程中发生了错误,可能就直接导致程序挂掉。就拿我来说吧,最开始做的一个项目,写代码时完全没有什么设计模式和编程原则的,经常所有操作都在主界面上,甚至sql语句都直接在Form类里。结果主界面控件很多,每个控件的操作也写在主界面里。包括一些小功能:托盘,判断网络连接,界面拖动等,全放在一起。最终完成项目后,主界面的类超过了一万行,其它子窗体的代码也是上千行。结果,修改bug的时候,刚修改好一个,又出现了另一个。搞得很累。

    当然,单一职责原则并不是教条地只能为类定义一个职责,而是要强调,在定义类职责时,必须考虑职责与对象之间的所属关系。职责必须恰当地表现对象的行为,而不至于破坏和谐与平衡的美感,甚至格格不入。换言之,该原则描述的单一职责指的是公开在外的与该对象紧密相关的一组职责。例如,在上述的例子中,要加一个写文件的功能,那么可以重新整理一下,新建一个FileManager类,它包括已有的ReadLineFile(),Wriete(),等其他与文件操作相关的这些方法,而不需要,读文件专门写一个类,写文件又专门写一个类,因为从职责的角度来讲,读文件和写文件两个方法是内聚的,完全符合单一职责原则中"一个类只做一件事"的要求。

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值