[Object-oriented] 相依性

前言 :

写程序的时候都会听到说,要降低程序之间的相依性。

 

程序之间的「相依性」,可以用下面简单的范例来理解。FunctionA里面使用了FunctionB,当FunctionB功能变更的时候,FunctionA就必须跟着做修改。这也就是说,「FunctionA相依FunctionB」。

 

static void FunctionA()
{
    FunctionB();
}

static void FunctionB()
{

}

以上面这个范例看起来,相依性不会是很大的问题,改就是了。但是当我们把问题放大,假设系统里有1000个Function。Function之间互相相依。当要更改1个Function内容并且维持整个系统正确运作,就必须要去检查其他999个Function是否需要跟着修改。这样的修改会是一场灾难,也是造成很多软件系统,改这边坏那边的主要原因。所以减少相依性,是提高程序代码质量很重要的一个环节。

 

在「结构化导向程序设计」Function与Function之间的相依性,可以透过追踪程序代码的方式来识别。但到了「面向对象程序设计」的环境下,虽然说也是可以使用追踪程序代码的方式,来识别对象之间的相依性。但却会受到继承、接口等等面向对象特性的影响,让识别对象之间相依性的这件工作变得非常复杂。

 

本篇使用UML类别图里的「关系」当作工具,阐述该如何透过UML类别图里的「关系」,来辅助开发人员识别面向对象程序的对象相依性。

 

说明 :

识别对象相依性一个简单的方法,就是「当两个类别A与B,分别存在不同的项目内。大幅修改了B之后,A如果需要跟着重新编译,那就是A相依B」。我们依照这个原则,来检视UML类别图里所定义的各种关系:

 

关联关系(Association)

using Association.ProjectB;

namespace Association.ProjectA
{
    public class ClassA
    {
        private readonly ClassB _b;

        public ClassA(ClassB b)
        {
            _b = b;
        }

        public ClassB B { get { return _b; } }
    }
}

 

namespace Association.ProjectB
{
    public class ClassB
    {

    }
}

上图与范例程序是UML类别图里定义的关联关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ClassB的时候,ClassA需要跟着重新编译。所以ClassA相依于ClassB。

 

依赖关系(Dependency)

using Dependency.ProjectB;

namespace Dependency.ProjectA
{
    public class ClassA
    {
        public void MethodXXX(ClassB b)
        {
            // TODO
        }
    }
}

 

namespace Dependency.ProjectB
{
    public class ClassB
    {

    }
}

上图与范例程序是UML里定义的依赖关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ClassB的时候,ClassA需要跟着重新编译。所以ClassA相依于ClassB。

 

概括关系(Generalization)

using Generalization.ProjectB;

namespace Generalization.ProjectA
{
    public class ClassA : ClassB
    {
        public ClassA()
            : base()
        {

        }
    }
}

 

namespace Generalization.ProjectB
{
    public class ClassB
    {

    }
}

上图与范例程序是UML里定义的概括关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ClassB的时候,ClassA需要跟着重新编译。所以ClassA相依于ClassB。

 

实现关系(Realization)

using Realization.ProjectB;

namespace Realization.ProjectA
{
    public class ClassA : IntefaceB
    {

    }
}

 

namespace Realization.ProjectB
{
    public interface IntefaceB
    {

    }
}

上图与范例程序是UML里定义的实现关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更IntefaceB的时候,ClassA需要跟着重新编译。所以ClassA相依于IntefaceB。

 

聚合关系(Aggregate)

using Aggregate.ProjectB;

namespace Aggregate.ProjectA
{
    public class ClassA
    {
        private readonly ItemB[] _itemBCollection;

        public ClassA()
        {
            _itemBCollection = new ItemB[2];
            _itemBCollection[0] = new ItemB();
            _itemBCollection[1] = new ItemB();
        }

        public ItemB[] ItemBCollection { get { return _itemBCollection; } }
    }
}

 

namespace Aggregate.ProjectB
{
    public class ItemB
    {

    }
}

上图与范例程序是UML里定义的聚合关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ItemB的时候,ClassA需要跟着重新编译。所以ClassA相依于ItemB。
*聚合关系语意较为复杂,范例程序只是简单示意。

 

组成关系(Composition)

using Composition.ProjectB;

namespace Composition.ProjectA
{
    public class ClassA
    {
        private readonly ItemB[] _itemBCollection;

        public ClassA(ItemB[] itemBCollection)
        {
            _itemBCollection = itemBCollection;
        }

        public ItemB[] ItemBCollection { get { return _itemBCollection; } }
    }
}

 

namespace Composition.ProjectB
{
    public class ItemB
    {

    }
}

上图与范例程序是UML里定义的组成关系,代表图形与范例程序。依照原则去检视程序代码可以看出,当变更ItemB的时候,ClassA需要跟着重新编译。所以ClassA相依于ItemB。
*组成关系语意较为复杂,范例程序只是简单示意。

 

后记 :

本篇的文章描述了,如何透过UML类别图里的「关系」,来辅助开发人员识别面向对象程序的对象相依性。当将对象依照职责分类成为独立Package的时候,对象之间的相依性也需要考虑进去,避免在Package之间有相依性杂乱的问题。当发现相依性杂乱时,则可以透过IoC、Facade等等手法来整理相依性。透过不断的整理相依性,就能慢慢提高程序代码的品质。

 

参考数据 :

维基百科 - 类别图

 

posted @ 2012-04-01 10:16  Clark159  阅读(1232)  评论(2编辑  收藏  举报