OOP的几个原则-----OCP:开闭原则(下)

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

Circle类

ExpandedBlockStart.gif View Code
  class Circle
    {
         private Guid _id;
         public Circle() { _id = Guid.NewGuid(); }

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

 

GUICommon类

ExpandedBlockStart.gif View Code
   static  class GUICommon
    {
         private  static List<Circle> _circles =  new List<Circle>();
         public  static List<Circle> Circles {  get {  return _circles; } }

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

         public  static  void AddCircles(List<Circle> 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类型

ExpandedBlockStart.gif View Code
  abstract  class Sharp
    {
         protected Guid _id;

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

         public  abstract  void Draw();
    }


新的Square类型

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

 

Circle类型同样继承自Sharp

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

GUICommon类 

ExpandedBlockStart.gif View Code
     static  class GUICommon
    {
         private  static List<Sharp> _sharps =  new List<Sharp>();
         public  static List<Sharp> Sharps {  get {  return _sharps; } }

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

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

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

 

有上面的示例可以看出,即使将来增加新的类型.我们只需要增加一个Sharp类的新派生类,这样DrawAllSharp方法就符合开闭原则,无需改变自身代码就可以扩展它的行为.其次,我们所做的工作只是创建新类,并且实现抽象类的所有方法,再也不需要为了找出更改的地方而在应用程序中到处搜索,这个方案不在是脆弱的.

同样,这个方法不在是僵化的.所有模块的代码不需要任何修改.仅仅是创建派生类实例的模块需要改动.除此而外,任何程序中重用DrawAllSharp方法时,不需要附带上两个具体的类型,只是需要创建自己的派生类,这个方案同样不再是顽固的.

但是...还是会存在一个问题,如何使圆在正方形之前输入呢?将具体的类抽象成Sharp类反倒成了一种障碍,虽然这个抽象是贴切的,但是应对这个问题它却不是最优的.很显然,在这个系统中,形状的顺序比形状的类型更具有实际意义.所以,无论模块多么封闭,总会存在一些无法封闭的变化,没有对与所有情况都贴切的类型.

如何解决形状顺序输出的问题呢?是不是在DrawAllSharp方法中对集合中每个形状类型进行判断,控制它们的顺序呢?难道需要这样实现?

 

ExpandedBlockStart.gif 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集合是可以排序的,我们可以将形状顺序的设置隔离到一个新的辅助类中,在每次按顺序输出之前对集合体进行排序.

 

ExpandedBlockStart.gif View Code
class SharpComparer:IComparer<Sharp>
    {
         private  static Dictionary<Type,  int> priorities =  new Dictionary<Type,  int>();

         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<Sharp> 成员

         public  int Compare(Sharp x, Sharp y)
        {
             int priority1 = PriorityFor(x.GetType());
             int priority2 = PriorityFor(y.GetType());
             return priority1.CompareTo(priority2);
        }

         #endregion
    }

 

 DrawAllSharp方法的修改

ExpandedBlockStart.gif View Code
public  static  void DrawAllSharp()
        {
            _sharps.Sort( new SharpComparer());
             foreach (Sharp s  in _sharps)
            {
                s.Draw();
            }
        }

对于上面的解决方案.每次顺序变化时,我们需要修改SharpComparer类中顺序的定义,而不再需要改变其他任何模块的代码.达到对顺序变化的封闭!

 

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

转载于:https://www.cnblogs.com/bit64/archive/2012/03/05/2379855.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值