软件体系结构复习

前言

总结了最近软件体系结构及设计模式的所学以及考试要点, 但是不是很严谨, 日后还需补充, 仅作参考

4+1视图

逻辑视图

功能需求(功能调用关系)(可以根据菜单设计)

最终用户

物理视图

拓扑结构

系统工程人员

开发视图

软件各层模块设计(页面-业务-底层(DB,网络,安全…))

编程人员

进程视图

画出一个流程的从前端到后端的各个可能产生关系的进程调用关系。

系统集成人员

场景视图

某个功能场景下,从用户操作到系统内部顺序的整个过程描述

软件体系结构

软件体系结构是对系统的高层设计,是指系统的基本组织结构,包括:组成系统的构件构件之间的连接关系、以及系统需满足的约束、原则等。

软件体系结构风格

软件体系结构风格是对软件体系结构的分类总结和描述一类系统组织方式的惯用模式。

体系结构风格定义一个词汇表一组约束

词汇表:包含构件连接件类型

约束:指出系统是如何将这些构件和连接件组合起来的。

体系结构风格反映了众多系统所共有的结构和语义特性,并指导如何将各个模块有效地组织成一个完整的系统

面向对象设计风格

这种风格建立在数据抽象和面向对象的基础上,数据的表示操作封装在一个抽象数据类型或对象中。

这种风格的构件是对象,或者说是抽象数据类型的实例。对象是一种被称作管理者的构件,因为它负责保持资源的完整性。对象是通过函数和过程的调用来交互的。

结构化设计风格

模块化设计,采用自顶向下的方法,将问题划分为多个相对独立、功能单一的模块,以功能块为单位进行程序设计。降低了程序复杂度,提高代码的重用性,使程序易于调试和维护、扩充

管道过滤器风格

每个构件(过滤器)有一组输入和输出,构件读输入的数据流,经过内部处理,然后产生输出数据流。这里的构件被称为过滤器,这种风格的连接件是数据流传输的管道,将一个过滤器的输出传到另一过滤器的输入

过滤器 → 管道 → 过滤器

层次风格

层次系统组织成一个层次结构,每一层为上层服务,并作为下层客户。在一些层次系统中,内部的层只对相邻的层可见。这种风格支持基于可增加抽象层的设计。允许将一个复杂问题分解成一个增量步骤序列的实现

构件在一些层实现了虚拟机

连接件通过决定层间如何交互的协议来定义

拓扑约束包括对相邻层间交互的约束。

//由于每一层最多只影响两层,同时只要给相邻层提供相同的接口,允许每层用不同的方法实现,同样为软件重用提供了强大的支持。

CS风格

C/S软件体系结构是基于资源不对等,且为实现共享而提出来的。

C/S体系结构定义了工作站如何与服务器相连,以实现数据和应用分布到多个处理机上

C/S体系结构有三个主要组成部分:数据库服务器客户端程序网络

客户端,服务请求者。不共享任何资源,但需要获取服务器内容功能

服务器运行一个或多个服务器计划,与客户分享他们的资源

BS风格

浏览器/服务器(B/S)风格就是上述三层C/S结构的一种实现方式,其具体结构为:浏览器/Web服务器/数据库服务器。B/S体系结构主要是利用不断成熟的WWW浏览器技术,结合浏览器的多种脚本语言,用通用浏览器就实现了原来需要复杂的专用软件才能实现的强大功能,并节约了开发成本

CS、BS混合风格

CS和BS各有优缺点,标准在不同地方也有不同的地位,使用习惯不同,还有遗留代码,所以会使用混合

其他

开闭原则

  1. 扩展是开放的。模块的行为可以扩展,当需求改变时,可以对模块进行扩展,增加新的功能
  2. 修改是封闭的。在对模块行为进行扩展时,不允许修改模块中已经存在的类的源代码

接口编程

所有的定义与实现分离。把业务抽象出来,具体实现通过该接口的实现完成。

在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部实现显得不那么重要;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,是系统设计的主要工作内容。

好处:业务逻辑清晰,代码易懂,方便扩展,可维护性强,符合开闭原则

设计模式

设计模式是对软件设计经验的总结,是对软件设计中反复出现的设计问题的成功解决方案的描述。设计模式可以用于软件体系结构的设计,以实现体系结构级的设计复用。设计模式可以使设计者更加方便地借鉴并直接使用已经经过证实的成功设计方案,不必花时间进行重复设计。帮助设计者更快更好地完成软件系统的设计工作

单例模式

无论是单例还是多例,对象属性和方法都不static。

而static的应该是获取单例/多例对象相关的方法或属性,

构造器都应该是私有的,因为不允许从getInstance外的其他方式构造对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ChairMan {

private static ChairMan chairmanInstance; //单例对象

private String name;
// 私有构造器,因为不允许外部初始化。
private ChairMan(String name){ this.name = name; }

/*
* 单例在第一次调用时初始化单例的static对象,后续不允许初始化。
*/
public static synchronized ChairMan getInstance() {
if (chairmanInstance == null) { //检查是否已经被初始化,未初始化则创建,否则已存在,跳过
chairmanInstance = new ChairMan("习近平");
}
return chairmanInstance;
}
//对象实例方法,不静态处理
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String toString(){ return name + super.toString(); }
}

多例模式

多个对象在类加载时就已经进行内部初始化,通过getInstance从static对象中获取对象,在构造器中限制类初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Marshal {
//用一个Map对象存放多例的对象,以便用key随机查找
private static Map<String, Marshal> marshalInstances = new HashMap<String, Marshal>();
private static int count = 0; //已初始化对象的数量,在构造方法中自增
private static final int MAX_COUNT = 5; //限制的多例对象数量上限,在构造方法中限制,超出则异常

private String name; //实例对象的属性,不采取static

//类加载时执行代码块,初始化多个对象
static {
String[] marshals = {"陈毅","罗荣桓","徐向前","聂荣臻","叶剑英","刘德华"};
try {
int i = 0 ;
for (String name : marshals) {
i++;
marshalInstances.put(name,new Marshal(name));
}
} catch (Exception ex) { System.out.println(ex); }
}

// 构造器
// 是私有的,因为不允许外部初始化
// 并通过已存在多例对象数量和已设上限来禁止超出上限数量后的初始化。
private Marshal(String name) throws Exception {
if (count >= MAX_COUNT) {
throw new Exception("不可继续创建新的元帅");
} else {
this.name = name;
count ++;
}
}
// 从Map中获取多例对象
public static synchronized Marshal getInstance(String name) {
return marshalInstances.get(name);
}

//对象实例方法,不用静态处理
public String toString(){ return name + "元帅"; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}

简单工厂方法模式

思想:在工厂类中用一个switch-case分辨需要的是哪一个类的对象,用一个父类型的引用获取新建的子类型对象。

工厂模式-简单工厂模式

缺点:

  1. 工厂类必须知道怎么样创建每个子类的对象,所以每次增加产品的时候,都需要修改工厂类代码,不符合开闭原则。
  2. 简单工厂类的工厂方法是静态的,不能被继承,只能是一个单独的类。

工厂模式

工厂模式-工厂模式

抽象工厂模式

对同结构的产品抽象成父类,不同的公司工厂生产其对应的产品。

有两种情况:

拓展产品不符合开闭原则

拓展工厂符合开闭原则

工厂模式-抽象工厂模式

安全组合模式

组合模式-安全组合模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Client  
{
public static void main(String[] args)
{
/**
* composite1
* / \
* leaf1 composite2
* / \
* leaf2 leaf3
*
* */
Component leaf1=new Leaf();
Component leaf2=new Leaf();
Component leaf3=new Leaf();
Composite composite1=new Composite();
Composite composite2=new Composite();

composite2.add(leaf2);
composite2.add(leaf3);
composite1.add(leaf1);
composite1.add(composite2);

composite1.doSomething();

}

}

一致型组合模式

与安全组合模式的区别在于所有的构建类都有相同的接口,各个类在接口上是没有区别的,客户端可以等同对待所有的对象。不必区分叶子节点和树枝节点,不需要强制类型转换。

缺点在于不够安全,Leaf(Person)需要屏蔽一些方法,并且抛出适当的异常。这样在客户类调用Leaf方法的时候可以做到部分方法可以什么都不做。

组合模式-安全组合模式

对象适配器模式

适配器模式-类适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
//对象适配器模式的适配器类
public class Adapter implements Ps2 {
// 依赖于USB类,在构造时存放一个USB的实例引用(这里体现了“对象”适配器模式)
private Usb usb;
public Adapter(Usb usb){
this.usb = usb;
}
//Target类的方法重写,调用了USB类的方法返回。(这里体现了适配)
@Override
public void isPs2() {
usb.isUsb();
}
}
1
2
3
4
5
6
public class Clienter {
public static void main(String[] args) {
Ps2 p = new Adapter(new Usber()); //这里实例Target类时在Adaptor构造器里传入实例
p.isPs2();
}
}

类适配器模式

适配器模式-类适配器

1
2
3
4
5
6
7
8
9
public class Adapter extends Usber implements Ps2 {	
//这里继承了被适配类USB,可以直接作为USB被构造和使用USB的方法
//由于实现了Target类,同时也可以作为Target类使用,但是实现是USB类的方法
@Override
public void isPs2() {
isUsb();
}

}
1
2
3
4
5
6
7
8
public class Clienter {
public static void main(String[] args) {
//直接作为Adapter被实例化,因为是继承了USB,所以不需要new USB,直接作为USB
Ps2 p = new Adapter();
p.isPs2();
}

}

ORM模式

对象-关系映射(Object Relational Mapping),把对象映射到关系模型数据库结构中去。在操作对象的时候,不需要再去和复杂的SQL语句打交道,只需简单的操作实体对象的属性和方法。

ORM技术是在对象和关系之间提供了一条桥梁,对象数据和数据库中的关系型的数据通过ORM来相互转化 。

IOC模式

控制反转

IoC模式,系统中通过IoC容器管理对象生命周期依赖关系等,使程序的配置和依赖性规范与实际的代码分开通过文本配置文件进行程序组件间相互关系的配置,而不用修改编译具体的代码

IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,利用Java反射,根据XML生成相应的对象。也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

// IOC模式把耦合从代码中移出去,放到统一的XML 文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中,也就是所谓“依赖注入DI”。

Spring框架基本原理和使用

Spring在Spring上下文中存放bean对象,对象在配置文件中声明。

使用时先从配置文件中获取上下文,然后可以用上下文content.getBean()获取bean对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<beans> <!-- 省略beans引用的头信息 -->
<bean id="teacher" class="cn.edu.scau.cmi.huangyanrong.spring.domain.Teacher">
<property name="id" value="liangzaoqing"/>
<property name="name" value="梁早清"/>
<property name="gender" value="男"/>
</bean>

<bean id="student" class="cn.edu.scau.cmi.huangyanrong.spring.domain.Student">
<property name="id" value="huangyanrong"/>
<property name="name" value="黄彦荣"/>
<property name="gender" value="男"/>
<property name="tutor" ref="teacher"/>
</bean>
</beans>
1
2
3
4
5
6
7
8
public class SpringClient {

public static void main(String [] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("codes/spring/config/applicationContext.xml");
Student student = (Student)context.getBean("student");
System.out.println(student);
}
}

Hibernate框架基本原理和使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<hibernate-mapping>
<class name="domain.Course" table="course">
<id name="id" type="java.lang.Long">
<column name="id" />
<generator class="assigned" />
</id>
<property name="name" type="java.lang.String">
<column name="name" />
</property>
<set name="students" inverse="true" table="select">
<key>
<column name="course" not-null="true" />
</key>
<many-to-many entity-name="domain.Student">
<column name="student" not-null="true" />
</many-to-many>
</set>
</class>
</hibernate-mapping>