软件危机

了解软件危机的表现形式,及其根源。了解老化软件的四个特性。

软件危机的表现形式:①软件不能按时完成;

②软件超出预算;

③软件不能正常运行;

④软件难以维护(使用周期短);

软件危机的问题根源:①开发效率低;

②产品质量差;

③产品难维护;

软件老化的四个特性:①过于僵硬(rigidity);

②过于脆弱(fragility);

③复用率低(immobility);

④黏性高(viscosity);

UML图

六字基本原则:高内聚、低耦合

类模块中高内聚、低耦合体现在

  • 信息隐藏(降低类之间的耦合)
  • 概念完整性(提高类的内聚性)

图类

他们分别是:依赖、关联、聚合、组合、继承、实现。他们的耦合度依次增强

依赖:方法参数或局部变量为另一个类

依赖关系的定义为:对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系。定义比较晦涩难懂,但在java中的表现还是比较直观的:类A当中使用了类B,其中类B是作为类A的方法参数、方法中的局部变量、或者静态方法调用。类上面的图例中:People类依赖于Book类和Food类,Book类和Food类是作为类中方法的参数形式出现在People类中的。

关联:

成员类型为另一个类
对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联关系。关联关系分为单向关联和双向关联。在java中,单向关联表现为:类A当中使用了类B,其中类B是作为类A的成员变量。双向关联表现为:类A当中使用了类B作为成员变量;同时类B中也使用了类A作为成员变量。

聚合

聚合关系是关联关系的一种,耦合度强于关联,他们的代码表现是相同的,仅仅是在语义上有所区别:关联关系的对象间是相互独立的,而聚合关系的对象之间存在着包容关系,他们之间是“整体-个体”的相互关系。

组合

 相比于聚合,组合是一种耦合度更强的关联关系。存在组合关系的类表示“整体-部分”的关联关系,“整体”负责“部分”的生命周期,他们之间是共生共死的;并且“部分”单独存在时没有任何意义。在下图的例子中,People与Soul、Body之间是组合关系,当人的生命周期开始时,必须同时有灵魂和肉体;当人的生命周期结束时,灵魂肉体随之消亡;无论是灵魂还是肉体,都不能单独存在,他们必须作为人的组成部分存在。

继承

继承表示类与类(或者接口与接口)之间的父子关系。在java中,用关键字extends表示继承关系。UML图例中,继承关系用实线+空心箭头表示,箭头指向父类。

实现

符号:定义:表示一个类实现一个或多个接口的方法。接口定义好操作的集合,由实现类去完成接口的具体操作。在java中使用implements表示。UML图例中,实现关系用虚线+空心箭头表示,箭头指向接口。

设计思维

契约设计含义

契约通过方法的前置条件、后置条件以及类的不变量描述。

  • 前置条件:需要方法的调用者保证。
  • 后置条件:需要方法的提供者保证。
  • 不变量:是类范围定义的,类的每个实例都必须保持或满足

理解基于契约设计能够保证软件正确性的特点,软件设计的三大质量目标中每一项分别通过哪些来保证?

正确性:正确性软件的基本要求,往往通过契约式编程来保障。

三大质量目标:重用性、扩展性、兼容性

三大质量目标的保证:通过SOLID原则和其他设计原则来保证。

SOLID原则

SOLID原则:要想将OOP的长处发挥出来,要想有贯彻总的设计理念,要想解决具体的设计问题,需要像SOLID原则来提供这种具体的行动准则和思考方法。

单一职责原则(SRP):一个类绝不要因为多于一个原因变化。

开闭原则(OCP):对软件实体(类、模块、函数)等扩展开放对修改关闭

里氏替换原则(LSP):引用了基类的功能,在使用派生类对象代替基类后应不受影响,

且不须知道具体为何种派生类。

接口隔离原则(ISP):客户不应该被强迫去依赖他们不使用的接口。

依赖倒置原则(DIP):高层模块不应依赖低层模块。不仅如此,二者都要依赖于抽象。

理解和掌握面向对象方法的优点。

优点:①提高软件产品的审美标准

 ②提高软件的各项特性

 ③延长软件的生命周期

理解和掌握瀑布、迭代模型两种主要的开发过程及其涵义。

瀑布模型:需求分析->设计->构建->系统集成与测试—>系统维护

理想化开发方式(线性过程)

迭代模型:

现实化开发方式(反复过程)

RUP迭代过程:业务建模->需求->分析及设计->实现->测试->部署

三种注入方式

依赖查找:一个对象在运行时与另一个对象建立链接,以便在运行时可以根据名称找到另一个对象。

**依赖注入:**一个对象并不能依赖查找那主动在注册中查到链接,而是被动地等待相关的关联。

**三种形式的注入:**构造注入、依赖注入、接口注入

架构定义

**架构:**针对特定问题给出的可复用架构设计方案。参考架构只是设计方案,供解决类似问题时参考。

**架构模式:**解决架构问题的已被证实有效的解决方案。架构模式是架构设计经验的总结。

**设计模式:**一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。

架构设计五步

  • 识别架构目标
  • 识别关键场景
  • 建立应用概貌
  • 识别关键问题
  • 定义候选方案

AOP

**横切关注点:**可能适用于所有逻辑层、组件和物理层的设计特性

例子:身份认证与授权、缓存、通信、配置管理、异常管理、日志、验证

设计模式:

适配者模式

把一个类接口转换成另一个用户需要的接口。

鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。

要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子!

1
2
3
public interface Duck {
void quack();
}
1
2
3
public interface Turkey {
void gobble();
}
1
2
3
4
5
6
public class WildTurkey implements Turkey {
@Override
public void gobble() {
System.out.println("gobble!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class TurkeyAdapter implements Duck {
Turkey turkey;

public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}

@Override
public void quack() {
turkey.gobble();
}
}
1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
Turkey turkey = new WildTurkey();
Duck duck = new TurkeyAdapter(turkey);
duck.quack();
}
}

装饰者模式

动态地扩展一个对象的功能,而不需要改变原始类代码的一种成熟模式。

案例:实现机动车接口(IAuto)的有敞篷车(Comvetible)和豪华轿车(Lumousine);对于任何类型汽车都可以加装不同配件(Equipment)类,有导航系统(Navigation),空调(AirConditioner),以及侧面气囊系统(SideAirBag);而且配件可以随意进行组合和搭配。并且支持扩展新的车辆或配件种类。

代理模式

控制对其它对象的访问。

通过代理对象增加一层间接性。代理对象实现与主题对象相同的接口,并且负责控制和增强对主题对象的访问。

  • 远程代理(Remote Proxy): 控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
  • 虚拟代理(Virtual Proxy): 根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
  • 保护代理(Protection Proxy): 按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
  • 智能代理(Smart Reference): 取代了简单的指针,它在访问对象时执行一些附加操作: 记录对象的引用次数;当第一次引用一个持久化对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。

组合模式

将对象形成树形结构来表现整体和部分的层次结构的成熟模式。

类图:

组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。

观察者模式

涉及到多个对象都对一个特殊对象中的数据变化感兴趣,而且这多个对象都希望跟踪那个特殊对象中的数据变化。

类图

主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。

工厂

简单工厂

1
2
3
public interface Fruit {
Integer cash();
}
1
2
3
4
5
6
public class Apple implements Fruit{
@Override
public Integer cash() {
return 2;
}
}
1
2
3
4
5
6
public class Banana implements Fruit{
@Override
public Integer cash() {
return 3;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Factory {
public Fruit getFruit(int type) {
if (type == 1) {
return new Apple();
} else if (type == 2) {
return new Banana();
} else {
return null;
}
}
public static void main(String[] args) {
Factory factory = new Factory();
Fruit fruit = factory.getFruit(1);
Fruit fruit2 = factory.getFruit(2);
System.out.println(fruit.cash());
System.out.println(fruit2.cash());
}
}

但这个简单工厂不符合太符合开闭原则的,因为工厂实际上是针对于调用方提供的,所以我们应该尽可能对修改关闭。

工厂方法

在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。

定义一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

1
2
3
4
public abstract class Factory {
abstract public Fruit getFruit();
}

1
2
3
4
5
6
7
public class AppleFactory extends Factory{
@Override
public Fruit getFruit() {
return new Apple();
}
}

抽象工厂

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。缺陷在于每添加一个新产品就要在抽象类进行修改,违反了开闭原则

1
2
public class AbstractProductA {
}
1
2
public class AbstractProductB {
}
1
2
public class ProductA1 extends AbstractProductA {
}
1
2
public class ProductA2 extends AbstractProductA {
}
1
2
public class ProductB1 extends AbstractProductB {
}
1
2
public class ProductB2 extends AbstractProductB {
}
1
2
3
4
public abstract class AbstractFactory {
abstract AbstractProductA createProductA();
abstract AbstractProductB createProductB();
}
1
2
3
4
5
6
7
8
9
public class ConcreteFactory1 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA1();
}

AbstractProductB createProductB() {
return new ProductB1();
}
}
1
2
3
4
5
6
7
8
9
public class ConcreteFactory2 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA2();
}

AbstractProductB createProductB() {
return new ProductB2();
}
}
1
2
3
4
5
6
7
8
public class Client {
public static void main(String[] args) {
AbstractFactory abstractFactory = new ConcreteFactory1();
AbstractProductA productA = abstractFactory.createProductA();
AbstractProductB productB = abstractFactory.createProductB();
// do something with productA and productB
}
}

单例

单例模式分为两种,一种为饿汉式,一种为懒汉式,

饿汉式采用直接实例化 Singleton 的方式,但这样如果在不使用的情况下会消耗资源

懒汉式采用的后面实例化,从而达到节约资源,当有多个线程访问进去的时候,为了保证获取到的都是同一个 Singleton.class,通过对其进行上锁,保证只有一个线程可以进去获取实例化,当实例化结束后,其他线程获取到的都是已经被实例化好的singleton

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}

class simple{
private static volatile simple instance;
private simple(){};
public static simple getInstance(){
if(instance == null){
synchronized (simple.class) {
if (instance == null){
instance= new simple();
}
}
}
return instance;
}
}

对象池模式

使用一个对象池类专门负责管理这种对象。客户类需要使用对象时,向对象池申请;客户类使用完毕,向对象池归还对象。对象池可以限制对象的数量,让希望获取对象使用的客户类排队等候;对象池也节省了创建对象的时间。

案例:出租车中心调度程序

内容:出租车中心需要管理出租车,中心需要给想坐出租车的乘客分派空车,使用后出租车回归到对象池。(典型的出租车中心调度应用),通过对象池模式解决。

创建型模式总结

  • 工厂方法模式:解决在框架代码或者其他地方无法创建具体对象,需要将对象的创建推迟到框架的应用代码或者扩展代码中完成。工厂方法模式因使用工厂方法而得名。叫虚构造器也很贴切。
  • 抽象工厂模式:解决需要使用的多种产品,在不同的系列中要配套出现和使用的问题。使用一个构造器类创建一个系列的多种产品。增加一个产品系列则添加一个具体构造器类。
  • 单实例模式:解决在应用中需要确保某个类只有一个实例的问题。确保一个类只有一个实例是为了节省资源或者保证数据的一致性和实现全局的共享。
  • 对象池模式:解决需要创建的对象比较耗时,同时往往又有数量限制(远程连接数,数据库连接数等)。对象池可以节省对象创建时间,同时也可以很好控制对象的总数。

设计模式总结

设计模式一般是为了得到某方面灵活性可变性。使用设计模式的效果是想要某方面可以变化而不用重新设计。

依据要达到的变化性目的,可将设计模式分为创建型模式结构型模式行为模式

从使用范围分,设计模式主要用于类就是类模式,通过继承关系实现,是静态的;主要用于对象,就是对象模式,通过关联实现,是动态的。

所学模式中,模板方法工厂方法类模式适配器模式可以用类继承实现,也可以用关联实现其他所有模式都是对象模式

适配器:让适用的接口变成可用;

桥梁:让抽象和对抽象的实现分开,二者可以独立演化;

装饰:动态地为类添加功能;

外观:为子系统提供简化的接口;

组合:对树形结构中的简单类和复合类用统一的方法处理。

代理:代理对象隐藏了真实对象,实现了真实对象接口。他可以将功能委托给真实对象实现,也可以为真实对象提供附加功能;

模板方法:在基类中规定算法结构或步骤,子类扩展可变步骤;

命令:用类封装命令,允许命令的生成和执行分离;

观察者:允许一个对象将自己状态变化通知给以来于其状态的对象

策略:对算法进行封装,进而可以替换;

中介者:允许对象之间通过中介者进行交流,减少同事类的耦合,进而可以独立变化;

状态:用类封装状态,依赖于状态的对象与状态类相关联并可实现状态的转变;

角色:允许一个对象动态地改变角色,并可以同时充当多种角色,或由多个对象充当一种角色。采用角色类关联主题类的设计;

访问者:通过双向依赖实现不同数据结构上不同处理方法的双委派

迭代器:在客户类不了解数据结构组成方式的情况下,遍历数据结构中的每个元素。

架构模式:

  • 逻辑架构模式:分层模式,管道与过滤器模式,黑板模式
  • 物理架构模式:C/S架构,三层架构,B/S架构,面向服务的架构,中介模式,微服务模式等。
  • 扩展模式:插件架构模式
  • 交互模式:MVC模式,表现-抽象-控制模式

SOA

SOA模式:松耦合、跨平台、易重用、易扩展

分布式系统架构:服务提供者、服务使用者和服务目录三种角色构成(cloud alibaba)

优点:

  • 服务组件封装了一项业务功能,加强了业务与技术之间的结合。
  • 将系统功能分解成小的服务,实现了模块化
  • 服务基于接口定义,大规模复用方便。
  • 跨平台性。

缺点:

  • 系统层次结构复杂;
  • 需要使用多种协议,增加了通信开销;
  • 对数据传输速率要求较高;
  • 服务定义基于业务的细分。业务建模影响了架构的成败。

MVC

**MVC模式:**将图形界面程序分成模型、视图、控制器三个模块。数据及数据处理由模型负责;视图用于显示数据;对用户输入的处理业务逻辑由控制器处理。三者实现了关注点分离。

  • 模型(model):由领域类构成。领域类封装(表示)了存储在数据库或文件中的应用数据及相关处理逻辑;
  • 视图(view):呈现模型数据的界面。当模型变化时,视图会跟着变;相同的模型状态可以有多个视图。
  • 控制器(controller):负责接受用户的输入,将输入进行解析并反馈给模型,通常一个视图具有一个控制器。

优点:

一个模型可以有多个视图。

模型可复用。

可提高开发效率和质量。

图类

静态模型:类图、对象图、组件图、部署图、包图

类图

类图:用来描述系统中各种类之间的静态结构。

类之间的关系:关联、依赖、继承实现等

对象图

和类图一样反映系统的静态过程,但它是从实际的或原型化的情景来表达的。对象图显示某时刻对象和对象之间的关系。一个UML对象图可看成一个类图的特殊用例,实例和类可在其中显示。

用例图

用例图是从用户(角色)的角度出发,描述角色和用例之间的关系。即:谁要使用系统,一级他们使用系统可以做什么。简单来说就是:谁,可以用此系统做什么。

时序图

序列图是用来显示你的参与者如何以一系列顺序的步骤与系统的对象交互的模型。顺序图可以用来展示对象之间是如何进行交互的。序列图将显示的重点放在消息序列上,即强调消息是如何在对象之间被发送和接收的。

活动图

活动图描述用例要求所要进行的活动,以及活动间的约束关系,有利于识别并行活动。能够演示出系统中哪些地方存在功能,以及这些功能和系统中其他组件的功能如何共同满足前面使用用例图的业务需求。