设为首页 收藏本站
查看: 1615|回复: 0

[经验分享] OOP的几个原则-----OCP:开闭原则(下)

[复制链接]

尚未签到

发表于 2015-6-16 12:17:40 | 显示全部楼层 |阅读模式
  上一篇着重介绍了开闭原则的概念,通过类图我们可以看出,如果不对Client和Server类进行解耦,当Client类需要使用另外一个Server类时,必须对相关代码进行修改.导致不必要的僵化性和脆弱性.下面将通过一个渐进的示例来展示如何运用开闭原则:
1.客户需要有一个在标准GUI上绘制圆应用程序.
Circle类


DSC0000.gif DSC0001.gif View Code

class Circle
    {
        private Guid _id;
        public Circle() { _id = Guid.NewGuid(); }

        public void Draw()
        {
            Console.WriteLine("ID: {0} 圆形绘制",_id.ToString("N"));
        }
    }  
GUICommon类


View Code

  static class GUICommon
    {
        private static List _circles = new List();
        public static List Circles { get { return _circles; } }

        public static void AddCircle(Circle c)
        {
            _circles.Add(c);
        }

        public static void AddCircles(List collection)
        {
            _circles.AddRange(collection);
        }

        public static void DrawAllSharp()
        {
            foreach (Circle c in Circles)
            {
                c.Draw();
            }
        }
    }

我们可以在Main函数中向GUICommon中添加Circle,并且可以调用DrawAllSharp方法绘制Circle.

2.当某一天,客户说他需要程序也能够输出正方形,并且需要圆在正方形之前被绘制出.

至少我们可以想出两种办法来修改我们的程序,要么我们继续按照原来的方式新建一个Square类,在GUICommon中添加它的集合,并且在绘制时按顺序执行.要么我们对

  Square类和Circle类进行抽象,来适应不断新增的变化.同时在GUICommon中也只对这个固定的抽象体进行操作,而不是对具体的类型.
抽象出的Sharp类型


View Code

abstract class Sharp
    {
        protected Guid _id;

        public Sharp() { _id = Guid.NewGuid(); }

        public abstract void Draw();
    }

新的Square类型


View Code

class Square:Sharp
    {
        public override void Draw()
        {
            Console.WriteLine("ID: {0} 正方形绘制", _id.ToString("N"));
        }
    }

Circle类型同样继承自Sharp


View Code

  class Circle:Sharp
    {
        public  override void Draw()
        {
            Console.WriteLine("ID: {0} 圆形绘制",_id.ToString("N"));
        }
    }

GUICommon类


View Code

    static class GUICommon
    {
        private static List _sharps = new List();
        public static List Sharps { get { return _sharps; } }

        public static void AddSharp(Sharp s)
        {
            _sharps.Add(s);
        }

        public static void AddSharps(List collection)
        {
            _sharps.AddRange(collection);
        }

        public static void DrawAllSharp()
        {
            foreach (Sharp s in Sharps)
            {
                s.Draw();  
            }
        }
    }

  有上面的示例可以看出,即使将来增加新的类型.我们只需要增加一个Sharp类的新派生类,这样DrawAllSharp方法就符合开闭原则,无需改变自身代码就可以扩展它的行为.其次,我们所做的工作只是创建新类,并且实现抽象类的所有方法,再也不需要为了找出更改的地方而在应用程序中到处搜索,这个方案不在是脆弱的.
  同样,这个方法不在是僵化的.所有模块的代码不需要任何修改.仅仅是创建派生类实例的模块需要改动.除此而外,任何程序中重用DrawAllSharp方法时,不需要附带上两个具体的类型,只是需要创建自己的派生类,这个方案同样不再是顽固的.
  但是...还是会存在一个问题,如何使圆在正方形之前输入呢?将具体的类抽象成Sharp类反倒成了一种障碍,虽然这个抽象是贴切的,但是应对这个问题它却不是最优的.很显然,在这个系统中,形状的顺序比形状的类型更具有实际意义.所以,无论模块多么封闭,总会存在一些无法封闭的变化,没有对与所有情况都贴切的类型.
  如何解决形状顺序输出的问题呢?是不是在DrawAllSharp方法中对集合中每个形状类型进行判断,控制它们的顺序呢?难道需要这样实现?
  

View Code

       public static void DrawAllSharp()
        {
            foreach (Sharp s in Sharps)
            {
                if (s is Circle)
                {
                    s.Draw();
                }
            }

            #region 应对Square类型所做的变化
            foreach (Sharp s in Sharps)
            {
                if (s is Square)
                {
                    s.Draw();
                }
            }
            #endregion
        }  
  3.客户又说他需要系统同样能够输出三角形,并且绘制顺序按照三角形,正方形和圆形.
  当再次面对改变时,DrawAllSharp方法依旧只针对具体的类来实现.如何做到在函数中对形状的顺序变化封闭?值得注意的是,封闭是建立在抽象基础之上的.在此处,我们需要建立一个顺序抽象体.让Sharp类的各个派生类都不知道顺序的变化.List集合是可以排序的,我们可以将形状顺序的设置隔离到一个新的辅助类中,在每次按顺序输出之前对集合体进行排序.
  

View Code

class SharpComparer:IComparer
    {
        private static Dictionary priorities = new Dictionary();
        static SharpComparer()
        {
            priorities.Add(typeof(Circle), 3);
            priorities.Add(typeof(Triangle), 1);
            priorities.Add(typeof(Square), 2);
        }
        private int PriorityFor(Type type)
        {
            if (priorities.ContainsKey(type))
            {
                return priorities[type];
            }
            return 0;
        }
        #region IComparer 成员
        public int Compare(Sharp x, Sharp y)
        {
            int priority1 = PriorityFor(x.GetType());
            int priority2 = PriorityFor(y.GetType());
            return priority1.CompareTo(priority2);
        }
        #endregion
    }  
DrawAllSharp方法的修改


View Code

public static void DrawAllSharp()
        {
            _sharps.Sort(new SharpComparer());
            foreach (Sharp s in _sharps)
            {
                s.Draw();
            }
        }
对于上面的解决方案.每次顺序变化时,我们需要修改SharpComparer类中顺序的定义,而不再需要改变其他任何模块的代码.达到对顺序变化的封闭!


最后,在许多方面,OCP都是面向对象设计的核心所在.遵循这个原则可以带来面向对象技术所声称的巨大好处:灵活性,可重用性以及可维护性.然而,并不是说只要使用一种面向对象语言就遵循了这个原则.对于应用程序中的每个部分都肆意地进行抽象同样不是一个好主意.正确的做法是,开发人员应该仅仅对程序中出现频繁变化的那些部分做出抽象.拒绝不成熟的抽象和抽象本身一样重要!!!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-77941-1-1.html 上篇帖子: OCP读书笔记(7) 下篇帖子: 集群、邮件、OCP
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表