论软件体系结构设计中系统质量属性的实现
软件质量的好坏,不仅要看系统是否能满足客户的功能性要求,也要看其是否能满足客户的非功能性要求,系统非功能性用质量性来描述,与软件体系结构设计相关的系统质量属性主要有 可用性、可修改性、性能、安全性、可测试性和易用性 ,在软件体系结构的设计过程中,为了使系统的设计方案能保证某种特定的质量属性和实现,需要采用一些针对性的具体设计策略,例如,对可用性质量属性,为了阻止错误发展成为故障或者为了恢复错误,往往采取主动冗余或被动冗余的设计决策,对于可修改性质量属性,为了提高系统的可修改和扩展性,往往采取局部化修改的设计策略等等。
软件体系结构质量属性:
成功的企业体系结构必须具备各种各样的质量属性。它可以帮助预先定义这些属性。这个属性集构成了一个蓝图,以指导分析、设计、编码、部署、维护、更新等阶段的工作。这种体系结构是对成型后的系统的预想。典型的质量属性包括(但并不局限于此)下列方面:
可修改性 (Modifiability) : 有了这种特性就可以很容易地对基础软件进行修改 ,是指能够快速地以较高的性价比对系统进行变更的能力。
质量属性 虽然不是系统的功能需求, 但是 用户的这些要求达不到,最终会要求 拒绝使用我们所提供的系统的 。 如何满足 这样的非功能需求 呢? 上面 所提到 的各个属性都是相互制约的,都会对系统产生消极的影响,甚至相互是矛盾的,就要求我们怎么去取舍,在构架的设计中去折中处理。
可修改性 :其实还是代码的质量问题,让架构对程序员写的代码做约束,限制程序员的随意性 。我们邮政储蓄系统采用了 客户机 - 服务器 ( C/S )的软件体系结构的设计。
为了使系统具有可修改性和可伸缩性,系统被分成客户机和服务器两个部分;有(很大)一部分计算是在服务器部分执行的,服务器可以在不依赖客户机的情况下进行修改,并可为多个客户机提供服务。
一、局部化修改——目标是减少由某个变更直接影响的模块的数量;
1、预期期望的变更(expected changes ):
确保canVote() 方法返回true或者false, 同时你也能写一个测试用来验证这个方法抛出的IllegalArgumentException异常。
public class Student { public boolean canVote(int age) { if (i<=0) throw new IllegalArgumentException("age should be +ve"); if (i<18) return false; else return true; } }
Guava类库中提供了一个作参数检查的工具类--Preconditions类,也许这种方法能够更好的检查这样的参数,不过这个例子也能够检查
2、维持语义一致性(semantic coherence ):
3、泛化模块(Generalize the module ):
4、限制选择参数(Limit possible options ):
二、防止连锁反应——目标是限制对局部化的模块的修改,以防止对某个模块的修改间接地影响到其他模块;
尽量维持现有接口或类的名字等不变,把要改动的模块尽量降到最低。推迟绑定时间在我的系统中还没有体现出来。
维持语义的一致性是保证模块中不同责任之间可以协同工作,不要太多的依赖于其他的模块。
1、信息隐藏(Hide information ):
2、维持现有接口(Maintain existing interfaces ):
3、限制通信路径(Restrict communication paths ):
4、使用仲裁者(Use an intermediary ):
采用迪米特法则可以有效防止连锁反应:
例子:
在美国电影《教父》中,教父如果为了除掉对手,会亲自动手吗?肯定不,教父会安排手下人处理。教父这样位高权重的人,会直接跟杀手安排任务吗?一般不会,他会跟手下的心腹说明,然后由手下人去执行。
这样一来,我们看看,先看看三个普通角色,被杀的人Person,杀手Killer,心腹CoreMember,如下:
/** * 某个人 * @author ljtyzhr * */ public class Person{ public String name; } /** * 杀手 * @author ljtyzhr * */ public class Killer{ public void kill(Person someone){ System.out.println(someone.name+"被杀死了"); } } /** * 教父身边的核心人员 * * @author ljtyzhr * */ public class CoreMember{ private Killer killer; }
事实上,核心人员直接与杀手打交道,教父只会与心腹打交道,如此,关系应该如下:
/** * 教父身边的核心人员 * * @author ljtyzhr * */ public class CoreMember{ private Killer killer; public void kill(Person someone){ killer.kill(someone); } }
家父持有对核心人员的引用,如下:
/** * 教父 * @author ljtyzhr * */ public class GodFather{ CoreMember coremember; public void kill(Person someone){ Killer killer = new Killer(); killer.kill(someone); } }
三、延迟绑定时间——目标是控制部署时间并允许非开发人员进行修改。
1、运行时注册(Runtime registration):
2、配置文件(Configuration files):
3、多态(Polymorphism):
4、组件更换(Component replacement ) :
5、遵守已定义的协议(Adherence to defined protocols) :
延时0.5秒后发送一个网络请求,首先想到了handler,结果出现这么一个错误,解决方案很简单,就是在线程里调用Looper.prepare(),然后调用Looper.loop()就可以了
private void sendMessageToClient(final StringBuilder s){ new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } sendToClient.sendDataToClient(s,clientSocketAddress);//网络请求必须在子线程 } }).start(); }