[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)

[Object-oriented] 相依性

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)

[Object-oriented] 相依性

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)

[Object-oriented] 相依性

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)

[Object-oriented] 相依性

using Realization.ProjectB;



namespace Realization.ProjectA

{

    public class ClassA : IntefaceB

    {



    }

}

 

namespace Realization.ProjectB

{

    public interface IntefaceB

    {



    }

}

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

 

聚合关系(Aggregate)

[Object-oriented] 相依性

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)

[Object-oriented] 相依性

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等等手法来整理相依性。透过不断的整理相依性,就能慢慢提高程序代码的品质。

 

参考数据 :

维基百科 - 类别图

 

你可能感兴趣的:(object)