软件工程思考题

软件设计原则

  1. 论述一下“针对接口编程”的好处。

    信息隐藏原则要求接口与实现分离接口可以理解为一个类所提供的所有服务。实现就是类自身实现服务的方式。不必是因为没有必要知道内情就可以协作工作;不应是因为如果类的实现细节让客户知道,第一增加客户的负担,导致程序的可理解性降低;第二可能使客户的某些行为依赖于这些实现细节,导致若类内部实现发生变化,会迫使客户跟着变化。

    信息隐藏非常重要的原因在于,它可以使各个子系统之间脱耦,从而允许它们独立地被开发、优化、使用、阅读及修改。

    信息隐藏可以促进软件的复用,由于每个模块都不依赖于其他模块而存在,因此每个模块都可以独立地在其他的地方使用。

  2. 论述一下“优先使用对象组合”的好处。

    若类B想获得类A的一些功能,不要首先想到让B去继承A,而是应当让B拥有A的一个对象。

    这么做简明易用。B类对A类的其他方法与继承情况没兴趣,能使用所需的特定方法就成。

    如果要继承,虽然也能使用特定方法,但不得不与整个继承体系紧紧地绑在一块了。

  3. 论述一下“抽象”与“软件的可重用性”的关系。

    抽象化是一种使软件更具有一般性、灵活性、可理解性和可重用性的主要技术。

    抽象化存在于多种不同的形式和层次中,是软件构思者与设计者的先行原则。

    抽象化是任何重用技术的本质特征。但是,抽象化具有艰难的技巧性,并且是一个软件系统获得成功的主要障碍。

设计模式
  1. 对于某个模式,问“何种情形下会考虑使用此模式”?
  2. 给出某种情形,问“采用何种模式比较适合?为什么?”
  3. 精确画出某个模式的结构图。
软件体系结构
  1. 软件体系结构的作用

    软件体系结构是软件设计过程的一个阶段,它关心的是如何将复杂的软件系统划分为主/子系统、以及如何规范子系统的构成性能

    一个外向目标:建立满足最终用户的系统需求;

    一个内向目标:即那里满足后期设计者需要的、易于系统实现、维护和扩展的系统部件构成。

    • 提高认识和理解体系结构,可以使系统的高层次关系的到全面表达和深刻理解;

    • 获得正确的体系结构常常是软件系统设计成功的关键;

    • 体系结构对于复杂系统的高层次性能分析至关重要;

    • 熟练掌握体系结构的概念和描述,可以使软件设计者之间、设计者和用户之间快速方便地交流知识、经验;

    • 在体系结构的全局思想指导下的系统维护、扩充和升级,不会因为修改和扩充而破坏整体的完整和一致性。

  2. MVC模式

    模型:模型用于管理应用程序域的行为和数据,并响应为获取其状态信息(通常来自视图)而发出的请求,还会响应更改状态的指令(通常来自控制器)。包含了应用问题核心数据、逻辑关系和计算功能。控制器依据输入/输出的需要调用这些操作,模型还为视图获取显示数据而提供了访问其数据的操作。

    视图:视图用于管理信息的显示。以不同的表达形式显示模型的数据和状态。每个视图有一个更新操作,它们可以被变化-传播机制所激活。当视图的更新操作被调用时,它还会反过来获得来自模型的数据。

    控制器:控制器用于解释用户的鼠标和键盘输入,以通知模型和或视图进行相应的更改。以事件触发的方式接收用户输入。控制器如何获得事件依赖于界面的运行平台。控制器通过事件处理过程对事件进行处理,并为每个输入事件提供了相应的操作服务,把事件转化成对模型或视图的激发操作。

    用户输入导致模型变化,并触发变更-传递机制的过程:

    1. 控制器在事件处理中接受用户输入事件,解释事件,并激活模型的相应服务方法;
    2. 模型执行所请求的服务,导致其内部数据和状态的变化;
    3. 模型找到在其中注册了的视图和控制器,并调用它们的update()方法,对它们进行通知;
    4. 每个注册了视图从模型中读取已经改变的数据,并更新其在视图中的显示;
    5. 每个注册的控制器从模型中读取已经改变的数据,根据设定关系允许或者禁止某些功能。
  3. C/S结构

应用框架
  1. 论述与其它软件复用技术的异同。

    • 与类库的对比

      相同点:都是可复用一组的类。

      不同点:

      • 类库中的一组类往往提供了一些软件的通用功能;而框架的这组类往往是针对某一个特定的应用领域。

      • 类库中的那些类之间往往关系松散,很少存在相互协作的关系;而框架中的一组类一定是代表了一个整体结构,它们之间的协作非常紧密。

      • 使用者使用类库时往往自己来定义程序的控制结构,自己去主动调用类库中的类方法;而框架使用者写的代码只能被框架调用。

      • 如果说类库像一堆零散砖头,而框架则就接近建筑的半成品了。

    • 与设计模式对比

      相同点:都是软件复用的形式。

      不同点:

      • 设计模式是比框架更小的体系结构元素,一个框架包括了多个设计模式,而反之绝非如此。

      • 设计模式比框架更抽象:框架能够用代码表示,能被直接执行和复用;而设计模式在每一次被复用时,都需要被实现,才能表示为代码。

      • 框架比设计模式更加特例化:框架总是针对一个特定的应用领域;而设计模式几乎能被应用于任何领域。

  2. 论述一下“框架”与“模版方法模式”、“钩子方法”、“凝点”、“热点”等概念之间的联系。

    从框架的复用机制来看,框架有三种类型:白盒、黑盒和灰盒三种,其中白盒和黑盒是两个极端,灰盒介于白盒、黑盒之间。

    白盒框架包含一些抽象类(未完成的类),这些类中包含若干空方法,称之为钩子方法(Hook Methods)。框架应用者必须继承(Inheritance)这些未完成的类,并在子类中复写钩子方法,提供特定于具体需求的有意义的实现。所以白盒框架的热点机制是继承和钩子方法。

    我们把某领域的程序族中反复出现的不变部分识别并“固化”到框架中,术语叫框架的“凝点”(Freeze Spots)。

    另一种重要任务是要把程序族中可变部分识别出来,在框架中给它们留下一些“空位”(Placeholder),术语称之为框架的“热点”(Hot Spots)。这些空位是留给应用程序员填写的,以支持特殊应用的特殊需求。

    凝点是框架提供了软件复用的根源,热点使软件复用成为可能。

软件重构
  1. 论述“软件重构”是如何与“软件设计”相互补充来解决过度设计问题的?

    设计阶段,人们总是预先仔细设计,力求得到一个灵活的解决方案,希望它能够承受所能预见的所有需求变化。

    过度设计问题:灵活的解决方案比简单的解决方案要复杂得多,所需的成本很高,最终得到的软件通常也会更难以维护。如果在所有可能出现变化的地点都建立起灵活性,整个系统的复杂度和维护难度将很难估计。但是,你无法预测到底哪些灵活性派不上用场,为了获得自己想要的灵活性,你不得不加入比实际需要更多的灵活性。

    有了重构,你就可以通过一条不同的途径来应付变化带来的风险。你仍旧需要思考潜在的需求变化,仍旧需要考虑灵活的解决方案。但是你不必再逐一实现这些解决方案,而是应该问问自己:“把一个简单的解决方案重构成这个灵活的方案有多大难度?”如果答案是“相当容易”,那么你就实现目前的简单方案就行了。

    使用重构,你仍然坐预先设计,但是不必一定找到完美的解决方案,只需要得到一个足够合理的解决方案就够了。

  2. 论述重构技术的意义和作用。

    重构的意义:

    • 重构改进设计
    • 重构使软件更容易理解
    • 重构有助于找到bugs
    • 重构有助于提高编程速度

    需要重构的程序结构:

    • 重复的代码
    • 过长函数
    • 过大的类
    • 过长的参数列表
    • 发散式变化
    • 依恋情结
    • switch语句
面向组件的软件开发
  1. 描述一下“面向对象技术”的缺陷。

    整体性的(Monolithic):而不是有明显的组件构成。

    封闭性的(Closed):而不能再扩展。

    私有性的(Proprietary):而不能在用于第三方。

    • 接口与实现的分离还不够彻底:类与子类的概念承担了太多的责任,用“类”来标示组件的接口,必然牵扯进很多实现方面的东西,使得“接口”与“实现”分割得不太彻底。
    • 重代码复用,轻复合机制:在对象技术应用的早期,复用功能的技术主要使用“继承”机制,而缺乏将现有对象“复合”在一起以完成新功能的认识。
    • 对象并不是天生为复用而设计的:被自然设计出来对象一般是私有性质的,不能被用在其它项目中。其根源在于:对象的接口机制只能保证其信息的单向对外隐藏,使得其客户不知实现细节;但是对象的实现却对其外部环境做了很多假设,即对象的外部环境对对象的实现没有进行信息隐藏,这导致此对象不能适应其它的运行环境。
  2. 软件组件都有哪些特性?

    更严格的接口与实现分离

    • 没有任何暴露在外的实现细节。
    • 只要保持组件的接口不变,组件的内部实现可以自由替换,而客户不受影响。
    • 有些组件甚至更进一步,具有“pluggable”实现,即组件实际工作的代码可以在软件运行时动态确定,主要技术手段是通过组件的配置设置。

    明确的环境依赖、反向控制

    • 组件不能对它使用的外在的运行环境做出过多的假设。一个反例是,组件需要某种服务,但它以实现类型保存着实现此种服务的对象。如果组件的运行必须得到外界环境的服务,这种对环境的依赖性必须明确化。
    • 为了避免对环境的依赖,有些组件被设计成不直接与环境打交道,而是通过“反向控制模式”,即:组件的容器(Container)负责打理组件运行所需的一切,组件处于被动状态:被请求时才运作。这也叫“好莱坞原则”。

    内省、可视化编辑

    组件一般会提供一种机制,可以让外界在一定范围内配置其功能。而对象则绝没有这种能力。通常这种机制是通过可视化图形配置工具来提供的。

  3. 组件是如何做到对外信息隐藏的?

    • 更严格的接口与实现分离
    • 明确的环境依赖、反向控制