GRASP:General Responsibility Assignment Software Patterns 通用职责分配软件模式。
首先我们先来区分下GRASP与GOF模式的区别,它们主要在什么时候用,用来做什么。在软件开发过程中,我们常说面向对象开发,面向对象思想应该贯穿整个软件开发生命周期。我们在将现实世界中的业务对象及业务功能抽象成软件系统中的系统对象过程中应该遵循使用GRASP模式。而在具体技术实现上应该遵循使用GOF设计模式,来实现系统功能。即GRASP主要使用在分析设计阶段,与具体技术无关;而GOF模式主要使用在开发阶段,与具体技术相关,它是对GRASP设计成果进行实现时而使用,是一种开发阶段的设计模式。
GRASP软件设计模式包括9个模式:创建者、信息专家、低耦合、控制器、高内聚、多态性、纯虚构、间接性、防止变异。
提示:以下是本篇文章正文内容,下面案例可供参考
(1)问题
怎样使得复杂性可管理?
(2)方案
分配一个职责,使得保持高内聚。
(3)分析
内聚是评价一个元素的职责被关联和关注强弱的尺度。如果一个元素具有很多紧密相关的职责,而且只完成有限的功能,则这个元素就具有高内聚性。此处的元素可以是类,也可以是模块、子系统或者系统。
在一个低内聚的类中会执行很多互不相关的操作,这将导致系统难于理解、难于重用、难于维护、过于脆弱,容易受到变化带来的影响。因此我们需要控制类的粒度,在分配类的职责时使其内聚保持为最高,提高类的重用性,控制类设计的复杂程度。为了达到低内聚,我们需要对类进行分解,使得分解出来的类具有独立的职责,满足单一职责原则。在一个类中只保留一组相关的属性和方法,将一些需要在多个类中重用的属性和方法或完成其他功能所需的属性和方法封装在其他类中。类只处理与之相关的功能,它将与其他类协作完成复杂的任务。
public class Genetic {
public Genetic(int s, int n, int g, float c, float m, frmGa f) {
//
}
// 初始化GA算法类
public void Init(int[] x, int[] y) {
//
}
// 初始化种群
private void InitGroup() {
//
}
private int Evaluate(int[] chromosome) {
//
}
// 计算种群中各个个体的累积概率,前提是已经计算出各个个体的适应度fitness[max],
// 作为赌轮选择策略一部分,Pi[max]
private void CountRate() {
//
}
// 挑选某代种群中适应度最高的个体,直接复制到子代中
// 前提是已经计算出各个个体的适应度Fitness[max]
private void SelectBestGh() {
//
}
// 复制染色体,k表示新染色体在种群中的位置,kk表示旧的染色体在种群中的位置
private void CopyGh(int k, int kk) {
//
}
// 赌轮选择策略挑选
private void Select() {
//
}
// 进化函数,正常交叉变异
private void Evolution() {
//
}
// 进化函数,保留最好染色体不进行交叉变异
private void Evolution1() {
//
}
// 类OX交叉算子
private void OXCross(int k1, int k2) {
//
}
// 交叉算子,相同染色体交叉产生不同子代染色体
private void OXCross1(int k1, int k2) {
//
}
// 多次对换变异算子
private void OnCVariation(int k) {
//
}
public void Solve() {
//
}
}
以上代码片段摘自TspGA(部分命名和访问修饰符有修改),这是用C#写的非常著名的用遗传算法解决旅行家问题的方案。部分实现代码限于篇幅已被省略,Genetic类是用来封装遗传算法的类,所有和遗传算法相关的内部全部被封装进这个类,符合高内聚原则,同时也符合信息专家原则。不过最终这个类中的代码有600行之多,可能会让人觉得代码的可维护性不高,甚至对这个类是否符合单一职责原则产生怀疑。变异概率和交叉因子的选择是否可以另起2个类来封装呢?
通用职责分配软件原则考虑的是职责的封装问题,高内聚原则鼓励将具有高度相关性的职责封装在一个类中,该案例将所有和遗传算法相关的职责封装进一个类中的做法我认为没有问题,即使它可能存在多个引起它变化的原因。所以我个人认为高内聚原则和单一职责原则并没有显著冲突,要看设计者的选择。