您的位置:首页 > 运维架构 > 网站架构

经典软件架构风格

2020-07-18 04:36 555 查看
  1. 软件架构
    软件架构是具有一定形式的结构化元素,即构建的集合,包括处理构件、数据构件和连接构件。
    处理构件:负责对数据进行加工
    数据构件:被加工的信息
    连接构件:把架构的不同部分组合连接起来
  2. 软件架构风格
    软件架构风格是描述某一特定应用领域中系统组织方式的惯用模式(idiomatic paradigm)
    架构风格定义了一个系统家族,即一个架构定义一个词汇表和一组约束。
    词汇表中包含一些构件和连接件类型
    约束指出系统是如何将这些构件和连接件组合起来的
  3. 通用架构风格分类
    (1)数据流风格:批处理序列,管道/过滤器
    (2)调用/返回风格:主程序/子程序,面向对象风格,层次结构。
    (3)独立架构风格:进程通信,事件系统
    (4)虚拟机风格:解释器,基于规则的系统
    (5)仓库风格:数据库系统,超文本系统,黑板系统
  4. 管道/过滤器风格
    (1)每个构建都有一组输入和输出,构建读输入的数据流,经过内部处理,然后产生输出数据流。
    (2)构建被称为过滤器,管道即是连接件,将一个过滤器的输出传到另一个过滤器的输入
    (3)过滤器必须是独立的实体,不能与其他过滤器共享数据,而且一个过滤器不知道它的上游和下游的标识。
    典型的管道过滤器的例子:UNIX shell编写的程序、传统的编译器(包含词法分析、语法分析、语义分析和代码生成)、J2EE中的过滤器等。
    优点:
    (1)使得构件具有良好的隐蔽性和高内聚、低耦合的特定
    (2)允许设计者将整个系统的I/O行为看成是多个过滤器行为的简单合成。
    (3)支持软件重用,只要提供适合在两个过滤器之间传送的数据,任何两个过滤器都可被连接起来。
    (4)系统维护简单,可扩展性好,新的过滤器可以添加到现有系统中来;旧的可以被改进的过滤器替换掉
    (5)允许对一些属性,如吞吐量、死锁等进行分析。
    (6)支持并行执行。每个过滤器是作为一个单独的任务完成,因此可与其他任务并行执行。
    缺点:
    (1)通常导致进程称为批处理的结构。这是因为虽然过滤器可增量式地处理数据,但它们是独立的,所以设计者必须将每个过滤器看成一个完整的从输入到输出的转换。
    (2)不适合处理交互的应用。当需要增量地显示改变时,这个问题尤为严重。
    (3)因为在数据传输上没有通用的标准,每个过滤器都增加了解析和合成数据的工作,这样就导致了系统性能下降,并增加了编写过滤器的复杂性。
    以下为java版代码示例:
package org.loyuru.architecture.style.pipefilter;

import java.util.ArrayList;
import java.util.List;

/**
* 管道/过滤器风格
* @author Teddy Lee
* @date 2020年3月14日
*/
public class PipeFilterDemo {

public static void main(String[] args) {
FilterChain filterChain = new FilterChain();
Filter filter1=new FilterImpl1();
Filter filter2=new FilterImpl2();
filterChain.addFilter(filter1);
filterChain.addFilter(filter2);
Data data=new Data();
data.setContent("1");
filterChain.doFilter(data);
}

}

//构建:过滤器
interface Filter {

public void doFilter(Data input, FilterChain chain);

}

//过滤器实现类
class FilterImpl1 implements Filter {

public void doFilter(Data input, FilterChain chain) {
Data output = new Data();
if ("1".equals(input.getContent())) {
System.out.println("Filter1执行");
output.setContent("2");
}
chain.doFilter(output);

}

}

//过滤器实现类
class FilterImpl2 implements Filter {

public void doFilter(Data input, FilterChain chain) {
Data output = new Data();
if ("2".equals(input.getContent())) {
System.out.println("Filter2执行");
output.setContent("3");
}
chain.doFilter(output);
}
}

//连接件:管道
class FilterChain {

List<Filter> filters = new ArrayList<Filter>();

int filterIndex = -1;

public void addFilter(Filter filter) {
filters.add(filter);
}

public void doFilter(Data input) {
filterIndex++;
if (filterIndex >= filters.size()) {
System.out.println("管道/过滤器执行结束,结果为:" + input.getContent());
}else {
filters.get(filterIndex).doFilter(input, this);
}
}

}

//数据pojo
class Data {
private String content = "";

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

}

执行结果:
Filter1执行
Filter2执行
管道/过滤器执行结束,结果为:3

  1. 面向对象风格
    面向对象风格建立在数据抽象和面向对象的基础上,数据的表示方法和它们的相应操作封装在一个抽象数据类型或对象中。这种风格的构件是对象,或者说是抽象数据类型的实例
    优点:
    (1)因为对象对其他对象隐藏它的表示,所以可以改变一个对象的表示,而不影响其他的对象
    (2)设计者可将一些数据存取操作的问题分解成一些交互的代理程序的集合
    缺点:
    (1)为了使一个对象和另一个对象通过过程调用等进行交互,必须知道对象的标识。只要一个对象的标识改变了,就必须修改所有其他明确调用它的对象。
    (2)必须修改所有显示调用它的其他对象,并消除由此带来的一些副作用。例如,如果A使用了对象B,C也使用了对象B,那么,C对B的使用所造成的对A的影响可能是料想不到的。

  2. 基于事件的隐式调用
    系统中的构件在一个或多个事件中注册,当一个事件被触发,系统自动调用在这个事件中注册的所有过程。这样,一个事件的触发就导致了另一模块中的过程的调用。
    优点:
    (1)为软件重用提供了强大的支持,当需要将一个构件加入现存系统中时,只需将它注册到系统的事件中。
    (2)为改进系统带来了方便,当用一个构件代替另一个构件时,不会影响到其他构件的接口。
    缺点:
    (1)构件放弃了对系统计算的控制。一个构件触发一个事件时,不能确定其他构件是否会响应它,而且,即使它知道事件注册了哪些构件的过程,它也不能保证这些过程被调用的顺序。
    (2)数据交换的问题。有时数据可被一个事件传递,但在另一些情况下,基于事件的系统必须依靠一个共享的仓库进行交互。在这些情况下,全局性能和资源管理便成了问题。
    (3)既然过程的语义必须依赖于被触发事件的上下文约束,关于正确性的推理就存在问题。
    以下为java示例代码:

package org.loyuru.architecture.style.event;

import java.util.ArrayList;
import java.util.EventListener;
import java.util.EventObject;
import java.util.List;

/**
* 基于事件的隐式调用
*
* @author Teddy Lee
* @date 2020年3月14日
*/
public class EventBasedImplicitCall {
public static void main(String[] args) {
NumberChangeSource source = new NumberChangeSource();
EventListener listener = new NumberChangeEventListener();
source.addEventListener(listener);
source.changeNumber(2);
// main方法并不知道listener是否会被调用,也不知道调用的哪个listener

}

}

//事件源
class NumberChangeSource {

private List<EventListener> listeners = new ArrayList<EventListener>();
int number = 0;

// 添加事件监听
public void addEventListener(EventListener eventListener) {
listeners.add(eventListener);
}

public void changeNumber(int number) {
if (number != this.number) {
this.number = number;
listeners.forEach(
listener -> ((NumberChangeEventListener) listener).handleEvent(new NumberChangeEvent(this)));
}
}
}

//事件
class NumberChangeEvent extends EventObject {
public NumberChangeEvent(Object source) {
super(source);
}
}

//事件监听
class NumberChangeEventListener implements EventListener {
public void handleEvent(EventObject e) {
e.getSource();
System.out.println("事件触发,数字更改为" + ((NumberChangeSource) e.getSource()).number);
}

}

执行结果:
事件触发,数字更改为2

  1. 分层系统
    层次系统组织成一个层次结构,每一层为上层服务,并作为下层的客户。
    优点:
    (1)支持基于抽象程度递增的系统设计,使设计者可以把一个复杂系统按递增的步骤进行分解。
    (2)支持功能增强,因为每一层至多和相邻的上下层交互,因此功能的改变最多影响相邻的上下层。
    (3)支持重用。只要提供的服务接口定义不变,同一层的不同实现可以交换使用。这样,就可以定义一组标准的接口,而允许各种不同的实现方法。
    缺点:
    (1)并不是每个系统都可以很容易地划分为分层的模式,甚至即使一个系统的逻辑结构是层次化的,出于对系统性能的考虑,不得不把一些低级或高级的功能综合起来。
    (2)很难找到一个合适的、正确的层次抽象方法。

  2. 仓库系统及知识库
    在仓库(repository)风格中,有两种不同的构件:
    (1)中央数据结构:说明当前状态
    (2)独立构件:在中央数据存储上执行
    控制原则的选取产生两个主要的子类。
    (1)若输入流中某类事件触发进程执行的选择,则仓库是一传统型数据库
    (2)若中央数据结构的当前状态触发进程执行的选择,则仓库是一黑板系统
    黑板系统的传统应用是信号处理领域,主要由3个部分组成:
    (1)知识源。知识源中包含独立的,与应用程序相关的知识,知识源之间不直接进行通讯,它们之间的交互只通过黑板来完成。
    (2)黑板数据结构。黑板数据是按照与应用程序相关的层次来组织的解决问题的数据,知识源通过不断地改变黑板数据来解决问题。
    (3)控制。控制完全由黑板的状态驱动,黑板状态的改变决定使用的特定知识。
    示例java代码如下:

package org.loyuru.architecture.style.blackboard;

import java.util.HashMap;
import java.util.Map;

/**
* 向黑板系统中输入学生成绩,黑板系统决定找哪个老师补课或领取奖状
*@author Teddy Lee
*@date 2020年3月15日
*/
public class BlackboardDemo {

public static void main(String[] args) {

Blackboard blackboard=new Blackboard();
blackboard.init();

Score score5=new Score();
score5.setChineseScore(60);
score5.setMathScore(60);
blackboard.process(score5);

Score score6=new Score();
score6.setChineseScore(50);
score6.setMathScore(60);
blackboard.process(score6);

Score score7=new Score();
score7.setChineseScore(60);
score7.setMathScore(50);
blackboard.process(score7);

}

}

//黑板系统
class Blackboard{

//知识源
Map<Score,Teacher> knowledge=new HashMap<Score,Teacher>();

//初始化
public void init() {
Score score1=new Score();
score1.setChineseScore(60);
score1.setMathScore(60);
Teacher teacher1=new Teacher();
teacher1.setName("教务处老师");
teacher1.setFunciton("发奖状");
knowledge.put(score1, teacher1);

Score score2=new Score();
score2.setChineseScore(50);
score2.setMathScore(60);
Teacher teacher2=new Teacher();
teacher2.setName("语文老师");
teacher2.setFunciton("补习语文");
knowledge.put(score2, teacher2);

Score score3=new Score();
score3.setChineseScore(60);
score3.setMathScore(50);
Teacher teacher3=new Teacher();
teacher3.setName("数学老师");
teacher3.setFunciton("补习数学");
knowledge.put(score3, teacher3);
}

//控制
public void process(Score score) {
knowledge.get(score).process();
}

}

//得分,黑板数据结构
class Score{
//语文得分
int chineseScore;

//数学得分
int mathScore;

public int getChineseScore() {
return chineseScore;
}

public void setChineseScore(int chineseScore) {
this.chineseScore = chineseScore;
}

public int getMathScore() {
return mathScore;
}

public void setMathScore(int mathScore) {
this.mathScore = mathScore;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + chineseScore;
result = prime * result + mathScore;
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Score other = (Score) obj;
if (chineseScore != other.chineseScore)
return false;
if (mathScore != other.mathScore)
return false;
return true;
}

}

//构件
class Teacher{

//老师名称
String name;

//老师的功能
String funciton;

public void process() {
System.out.println(name+funciton);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getFunciton() {
return funciton;
}

public void setFunciton(String funciton) {
this.funciton = funciton;
}

}

结果:
教务处老师发奖状
语文老师补习语文
数学老师补习数学

  1. C2风格
    C2(Compoment-Connector)架构风格可以概括为:
    通过连接件绑定在一起的按照一组规则运作的并行构件网络。
    C2风格中的系统组织规则如下:
    (1)系统中的构件和连接件都有一个顶部和一个底部。
    (2)构件的顶部应连接到某连接件的底部,构件的底部则应连接到某连接件的顶部,而构件与构件之间的连接是不允许的
    (3) 一个连接件可以和任意数目的其他构件和连接件连接。
    (4)当两个连接件进行直接连接时,必须由其中一个的底部连接到另一个的顶部。
    特点:
    (1)系统中的构件可实现应用需求,并能将任意复杂度的功能封装在一起。
    (2)所有构件之间的通信是通过以连接件为中介的异步消息交换机制来实现的。
    (3)构件相对独立,构件之间依赖性较少。系统中不存在某些构件将在同一地址空间内执行,或某些构件共享特定控制线程之类的相关性假设。

示例java代码如下:

package org.loyuru.architecture.style.c2;

/**
* C2(Componet-Connector)架构风格
* @author Teddy Lee
* @date 2020年3月15日
*/
public class C2Demo {
public static void main(String[] args) {
Componet componet1 = new Component1();
Componet componet2 = new Component2();
Connector connector = new Connector();
componet1.setBottom(connector);
connector.setTop(componet1);
connector.setBottom(componet2);
componet2.setTop(connector);

componet1.process(0);

}

}

interface Componet {
public void process(int num);

public void setTop(Connector top);

public void setBottom(Connector bottom);
}

//构件1
class Component1 implements Componet {
Connector top;
Connector bottom;

public void process(int num) {
num++;
System.out.println("Component1 process " + num);
bottom.connectBottom(num);
}

public Connector getTop() {
return top;
}

public void setTop(Connector top) {
this.top = top;
}

public Connector getBottom() {
return bottom;
}

public void setBottom(Connector bottom) {
this.bottom = bottom;
}
}

//构件2
class Component2 implements Componet {
Connector top;
Connector bottom;

public void process(int num) {
num++;
System.out.println("Component2 process " + num);
}

public Connector getTop() {
return top;
}

public void setTop(Connector top) {
this.top = top;
}

public Connector getBottom() {
return bottom;
}

public void setBottom(Connector bottom) {
this.bottom = bottom;
}
}

//连接件
class Connector {
Componet top;
Componet bottom;

public void connectBottom(int num) {
bottom.process(num);
}

public void connectTop(int num) {
top.process(num);
}

public Componet getTop() {
return top;
}

public void setTop(Componet top) {
this.top = top;
}

public Componet getBottom() {
return bottom;
}

public void setBottom(Componet bottom) {
this.bottom = bottom;
}

}

结果:
Component1 process 1
Component2 process 2

结束:
需要代码的可从:https://gitee.com/loyuru/java-base-demo.git 处下载

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: