软件体系结构

软件体系结构

软件体系结构:常被称为构架或架构,指可预制和可重构的软件框架结构。 软件体系结构=构件+连接件+约束

构件:是可预制和可重用的软件部件,是组成体系结构的基本计算单元或数 据存贮单元。

连接件:是可预制和可重用的软件部件,是构件之间的连接单元。

约束:用来描述构件和连接件之间的关系。 除了构件,连接件和约束3个最基本的组成元素,软件体系结构还包括端口和角色两种元素。

端口(port):每个端口表示了构建和外部环境的交互点。端口是构件与外部世界的一组交互的接口。

角色(role):连接件的接口由一组角色组成,连接的每个角色定义了该 连接表示的交互的参与者。

软件体系结构::=软件体系模型|软件体系风格
软件体系模型::=(构件,连接件,约束) 
构件::={端口1;端口2;...;端口n} 
连接件::={角色1;角色2;...;角色m} 
约束::={(端口i,角色j),...} 
体系结构风格::={管道过滤器,客户服务器,...,解释器}

构件(component)是指具有一定功能,可明确辨识的软件单 位,可以是计算单元,也可以是数据单元

构件和类区别和关系

构件和类的区别:构件是类的软件实施。类是代表一组属性和操作的抽象实体。类和构件的一个重要关系是:一个构件可以是多个类的实施

对构件和构件建模的意义

使客 户能够看到最终系统的结构;让开发者有一个目标;让编写技 术文档和帮助文件的技术人员能够理解所写的文档是关于哪方面内容的;利于重用等。

三种构件

部署构件:已编译和可执行的代码它形成了可执行系统的基础。例如动态链接库、二进制可执行体、Active X控件等。

工作产品构件:需求规格说明、设计文档、测试用例、源代码它是部署构件的 来源,如数据文件和程序源代码。

执行构件:执行构件是指可执行系统产生的结果,它们是在运行时根据部署构件和输入数据产生的。执行构件(Execution Component),是可运行系统产生的结果。执行构件是软件系统的用户界面和输出结果,它们向用户呈现系统的功能和数据。执行构件包括各种可视化界面、报表、日志文件。

连接件的概念

复杂的情况下,构件间交互的处理和维持都需要连接件来实现。

常见的连接件有管道(pipe,管道过滤器体系结构中) 通信协议或通信机制(客户服务器体系结构中)

连接件的特性

连接件的主要特性有可扩展性、互操作性、动态连接性和请求响应特性。

可扩展性:是连接件允许动态改变被关联构件的集合和交互关系的性质。

互操作性:指的是被连接的构件通过连接件对其他构件进行直接或间接操 作的能力

动态连接性:即对连接得动态约束,指连接件对不同的所连接构件实施不 同的动态处理方法的能力。

请求响应特性:包括响应的并发性,时序性。在并发或并发系统中,多个构件有可能并行或并发地提出交互请求,这 就要求连接件能够正确协调这些交互请求之间的逻辑关系和时序关系。

连接件的接口

连接件(Connector)是用于连接不同构件之间的抽象软件元素。连接件与所连接构件之间的交互是通过连接件的接口(Interface)来实现的,连接件的接口由一组交互点构成,这些交互点被称为角色(Role)。

主动角色是指向连接件发出请求或指令的构件。 被动角色是指接收连接件请求或指令的构件。

约束的概念及特点

体系结构约束提供 限制来确定构件是否正确连接,接口是否匹配, 连接件构成的通信是否正确。

概念

软件体系结构是具有一定形式的结构化元素(element),即构件的集合。(SA看成软件设计中的一个层次,且在该层设计属于宏观设计,不会考虑算法与数据结构这些细节内容的设计)处理构件数据构件连接构件。处理构件负责数据加工,数据构件是被加工的信息,连接构件把体系结构的不同部分组合连接起来。

「将SA中的所有元素均看成构件并将其加以分类,将 构件既看成计算单元,也看成数据(存储)单元,还可 以看成结连单元。」

3个构件:处理构件,数据构件,连接构件 4个视图:概念视图「主要构件及它们之间的关系」,模块视图「功能分解与层次结构」,运行视图『一个系统的动态结构』,代码视图『各种代码和库函数在开发环境中的组织』

软件体系结构风格

软件体系结构风格是一种通用的、可重复使用的、结构化的体系结构设计模式.常见的软件体系结构风格包括层次结构、客户端-服务器、管道和过滤器、事件驱动、微服务、REST

管道和过滤器(Pipe and Filter)

将系统分解为一系列能够处理数据流的过滤器,并通过管道将数据流从一个过滤器传递到另一个过滤器,实现对数据流的处理和转换。

管道通常是一个缓冲区或队列。 处理步骤都被封装在一个过滤器组件。

设计词汇表
构件:过滤器。对输入流进行处理、转换,处理后的 结果在输出端流出
连接件:位于过滤器之间,起到信息流的导管的作用, 被称为管道。
案例:
处理或转换输入数据流的系统【注意: 这是一类系统,具体实例如编译器、Word文本编辑器和建模 工具等】,如果把这样的系统作为单个构件来实现是困难的。

解决:管道和过滤器体系结构风格。

上一个构件的输出是下一个构件的输入,即一个构件中 处理步骤的输出是下一个构件处理步骤的输入。一个处理步骤可由一个过滤器实现,或多个处理步骤也 可由一个过滤器实现,该处理步骤或者处理数据或者转化数据。数据源过滤器和数据池(过滤器)之间通过管道顺序地 连接起来。
  • 后续的过滤器可以从当前过滤器中取出数据(被动式过滤器(passive filter))
  • 当前过滤器可向后继过滤器推送新的输入数据(被动式过滤器(passive filter)
  • 过滤器处于活跃状态,不断地从前面的过滤器中取出 数据流,经过加工后,再向后续的过滤器推送数据。(主动式过滤器(active filter))

被动式过滤器受函数调用或过程调用的激发而工作 主动式过滤器受独立程序或线程任务的激发而工作。 ```go

package main

import “fmt”

func MaxFilter(data []string) string { // 被动式过滤器,接收字符串列表并返回最长字符串 if len(data) == 0 { return “” } maxStr := data[0] for _, str := range data { if len(str) > len(maxStr) { maxStr = str } } return maxStr }

func main() { data := []string{“apple”, “banana”, “orange”, “kiwi”} result := MaxFilter(data) fmt.Println(result) }


```go
package main

import (
    "fmt"
    "time"
)

type ActiveFilter struct {
    interval time.Duration
    callback func()
    stopCh   chan bool
}

func NewActiveFilter(interval time.Duration, callback func()) *ActiveFilter {
    return &ActiveFilter{
        interval: interval,
        callback: callback,
        stopCh:   make(chan bool),
    }
}

func (f *ActiveFilter) Run() {
    ticker := time.NewTicker(f.interval)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            // 定时处理数据
            f.callback()
        case <-f.stopCh:
            // 停止处理数据
            return
        }
    }
}

func (f *ActiveFilter) Stop() {
    f.stopCh <- true
}

func main() {
    filter := NewActiveFilter(time.Second, func() {
        fmt.Println("ActiveFilter is running...")
    })
    go filter.Run()
    time.Sleep(5 * time.Second)
    filter.Stop()
    fmt.Println("ActiveFilter stopped.")
}

数据源: 数据源或者 可主动地把数据推送给过滤器,或者当过滤器需要时被动地提供数据。

数据池: 从过滤器收集计算结果并输出。数据池有两类 变种,主动式数据池从过滤器中“拉出”数据,被动式数据池允许其前 面的过滤器向其“推入”或“写入”结果

管道过滤器的类型

管道-过滤器的类型:管线(pipeline,如UNIX命令链),有名管道 (namedpipes,如文件,这样做的目的是为了限制数据流传输的方式)

管线是把过滤器严格限制为单输入、单输出的类型。这样,系统的拓扑结 构只能是线性的序列。多数管道-过滤器的应用属于这种简单结构。 有名管道是过滤器之间通过有名称的管道(例如文件)进 行数据传送的系统。有名管道限制了过滤器之间数据传 送只能发生在已经命名的管道中 风格不变性:每次加工完成之后, 过滤器都会统一回到的原始等待状态

独立性:设计和使用不对其相连的任何过滤器实施限制。

管道过滤器的优点

1-系统可以通过用户实施构件交换或改变处理 步骤进行重组,以适应新的需求。

2-系统可以利用构件独立性,通过更换和添加构件来维 护和升级。

3-具有明确独立输入/输出关系(即不变性)的构件很 容易实现重用。

4-系统具有自然的并发特性

5-能够支持快速原型系统的设计实现。

6-有清晰的拓扑结构

它允许设计者将一个系统的整体输入/输出行为理解为各个独立过滤器行为的一个简单的合成。 (2)该体系结构支持重用。(3)维护系统和增强功能容易。

管道过滤器的缺点

1-共享状态信息的代价高而且不灵活,因而不适于需要 共享大量数据的应用设计【如大数据应用】。

2-不适应交互式应用系统的设计和运行。过滤器是对输 入的批量转换处理,由于限制了每一批数据的输入个数、 形式、时间,从而限制了输出的形式,该类系统特别不 适于设计交互式应用。

3-由于构件之间通过特定格式数据的流动而支持相关的 处理工作,因此数据格式的设计和转换是这类应用系统 设计中的主要方面

4-难以进行错误处理

5-希望通过本风格所具有的并行运行特征而 获得高运行效率,这往往是行不通的。

例1.一个典型的管道-过滤器体系结构的例子是Unix shell程序。Unix既提供一种符号,以连接各组成部分 (Unix的进程),又提供某种进程运行时机制以实现管道。 例2.另一个著名的例子是传统的编译器。在该系统中, 一个阶段(包括词法分析、语法分析、语义分析和代码生 成)的输出是另一个阶段的输入

(1)容易导致批处理方式。(2)在维护或者响应两个分离但相关的数据流时,需进 行同步。(3)增加了分析与编码的工作量,增加了复杂性,降低了性能

管道过滤器的特征/词汇表

词汇表是[构件=过滤器;连接件=管道]

特征:

其一,过滤器一定是独立的实体,即各个过滤器之间不能 共享状态。 其二,过滤器与其连接的上下游过滤器相互独立

管道过滤器的种类

管线、有名管道和受约束的管道 三类。 管线限制过滤器的拓扑结构只能是线性序列。 受约束的管道限制在管道中流动的数据量。 有名管道要求在两个过滤器之间流通的数据经过严格地 定义(如文件)。

数据抽象和面向对象组织风格(Data Abstraction and Object

将数据和操作进行抽象和封装,形成对象,并通过对象之间的交互来实现系统的功能。数据抽象风格是特化(变异)后的面向对象 风格。数据的表示及其相关操作被封装为抽象数 据类型(Abstract Data Type,ADT)。

抽象数据类型ADT只具有封装性,不具有继承性和多态性

构件:对象 连接件:通过过程调用(方法)来实现

数据抽象和面向对象组织风格的优点

(1)隐藏实现细节,在不影响其使用者的情况下 允许修改对象。 (2)对子程序和数据的包装使得设计者可以分解 问题,把系统转化为交互的代理集合来处理。 (3)对象可以是多线程的可以是单线程的。

数据抽象和面向对象组织风格的缺点

(1)过程/方法调用依赖于对象标识。一个对象必 须知道与之交互的对象标识。 (2)不同对象的操作关联性弱。

隐式调用/消息(Implicit Invocation/Messaging)

通过解耦合系统的组件,使得系统的各个部分可以独立地进行操作,并通过消息传递来实现系统的协作和协调。 不直接调用一个过程/方法,而是声明或广播可调用 的过程/方法对应的事件 系统中的其他构件可以把某一过程/方法注册为与它所关心 的事件相关联。 当某一事件发生时,系统会调用所有与之相关联的过程, 即对一个事件的激励将隐式地导致了对其他模块的过程调用。 这样,事件声明或广播实际上就起到了与“隐式调用”事件进行的作用。

构件:模块。 连接件:对事件的显式或隐式调用。

隐式调用/消息的优点

(1)事件广播者不必知道哪些构件会被事件影响,构件之间关系弱。 (2)隐式调用有助于软件复用。 (3)系统的演化、升级简单。

隐式(事件)调用

(1)构件对系统进行的计算放弃了主动控制。 在有共享数据存储的系统(如文件系统或数据库系统) 中,资源管理器的性能和准确度成为十分关键的因素。 (2)很难对系统的正确性进行推理,因为声明或广播某个 事件的过程的含义依赖于它被调用时的上下文环境。

隐式(事件)调用的应用

  • 在程序设计环境中用于集成各种工具的设计。 - 在数据库管理系统用于检查数据库的一致性约束条件。 - 支持在用户界面中分离数据和表示。

分层系统风格

将系统分解为一系列层次结构,每个层次负责不同的功能和服务,并通过接口进行交互和通信,实现系统的分层和模块化。

分层系统风格的优点

(1)由于对层次的邻接层数目进行了限制,所以系统易于改进 和扩展。 (2)每一层的软件都易于重用,并可为某一层次提供多种可互 换的具体实现。 (3)分层系统所支持的设计体现了不断增加的抽象层次,这样, 一个复杂问题的求解被分解为一系列递增的步骤。

分层系统风格的缺点

系统的分层可能会带来效率方面的问题。 (2)应当如何界定层次间的划分是一个较为复杂 的问题。

分层系统风格的应用

分层系统常用于通信协议。最著名的分层风 格的体系结构的例子是ISO的OSI模型。

仓库(Repository)

将系统中的数据集中存储到一个中央仓库中,通过仓库的接口来对数据进行访问和操作,实现对数据的管理和控制。

构件:一个中央数据结构(可表示当前状态;)一个独立构 件的集合,它对中央数据结构进行操作。

仓库(Repository)的优点

(1)便于多客户共享大量数据,它们不用关心数据何时有 的、谁提供的、怎样提供的。 (2)既便于添加新的作为知识源代理的应用程序,也便于 扩展共享的黑板数据结构。

仓库(Repository)的缺点

不同的知识源代理对于共享数据结构要达成一致,而 且,这也会造成黑板数据结构的修改较为困难。 (2)需要一定的同步/家锁机制保证数据结构的完整性和 一致性,增大了系统复杂度。

仓库(Repository)的应用

例如,语音识别、模式识别、三维分子结构建模。 适用场景:它的数据和处理分布在一定范围内的多个构件上,构件之间通过网络连接。

客户/服务器(Client/Server)风格

服务器构件:向多个客户提供服务,它永远处于激活状态,监听用 户请求。 客户构件:向服务器构件请求服务 连接件:某种进程间通信机制。理想情况下,这种访问是透明的,即客户和服务器可以运行在 同一台机器上,也可以跨进程、跨机器进行

客户/服务器(Client/Server)风格的优点

(1)有利于分布式的数据组织。 (2)构件间是位置透明的,客户和服务器都不用考虑对方的运行位置。 (3)便于异质平台间的融合与匹配,客户和服务器可以运 行不同的操作系统。 (4)具有良好的可扩展性,易于对服务器进行修改、扩展 或增加服务器

客户/服务器(Client/Server)风格的缺点

客户必须知道服务器的访问标识,否则很难知道有哪些服务

连接方向

连接的方向是从客户端指向服务器,这是主连接方向。在复杂系统的某些情 况下,要求从服务器指向客户端(即这之间建立反向通 信)。例如,服务器需要将公共信息发送给所有的客户, 即所谓消息广播。

浏览器/服务器风格

浏览器/服务器(Browser/Server,B/S)风格具体结构为:浏览器 /Web服务器/数据库服务器。

浏览器/服务器风格优点

(1)有利于在限定的功能范围内查询组织相 关信息(2)应用程序的维护量大大减少

浏览器/服务器风格的缺点

(1)缺乏对动态页面的支持能力,没有有效的数据库处理 功能。 (2)系统扩展能力差,安全性难以控制。 (3)系统响应速度慢。 (4)数据的动态交互不强,不利于在线事务处理应用

解释器(Interpreter)

通过定义一种语言和相应的解释器,将输入的语句或表达式转换为可执行的指令序列,并执行这些指令序列,实现对输入的解释和执行。

源程序代码处理构件:处理源程序代码,可以是编辑器或产生源代码的程序。

程序伪代码处理构件:这个构件负责将源程序代码转换成可以快速被解释执行的中间代码。

控制构件:这个构件负责处理解释器的输入和输出,向解释器提供输入,并处理解释器的输出

解释器构件:分析代码的结构和语义,按照语义的要求完成响应的动作。例如,它可以执行中间代码、计算表达式、控制流程、处理异常等。

连接件:过程调用和直接存储器访问。

解释器(Interpreter)风格的优点

(1)有助于应用程序的可移植性和程序设计语言的跨平台能力。 (2)可以对未实现的硬件进行仿真。

解释器(Interpreter)风格的缺点

额外的间接层次带来了系统性能的下降。

解释器(Interpreter)风格的应用

程序设计语言的编译器,比如Java、Smalltalk等。 基于规则的系统,比如专家系统领域的Prolog等。 脚本语言,比如Perl等。

异质体系结构

过程控制(Process Control)

通过定义一组规则和流程来控制和管理系统中的业务流程和操作,实现对系统的流程和行为的控制和管理。

分布式系统(Distributed System)

将系统分解为多个节点,并通过网络连接进行通信和协作,实现系统的分布式和协同工作。

体系结构风格的好处

  • 促进了设计重用。
  • 代码重用
  • 标准化的风格

体系结构风格解决的问题:

1.设计词汇表是什么?或者构件和连接件的类型是什么? 2.可容许的结构模式是什么? 3.基本的计算模型是什么? 4.风格的基本不变性是什么? 5.其使用的常见例子是什么,或具体应用案例是什么? 6.使用此风格的优缺点是什么?
7.其常见特例是什么?

软件体系结构设计方案

基于数据共享的设计策略

对如下内容的支 持比较弱的:总体算法改变、数据表示修改和(构件的)重用。 对如下内容的支持比较强:由于凭借对共享数据的直接访问, 这种方案可以获得相对来说的好性能,同时,新增加的处理功 能也相对而言比较容易访问这些共享数据。

基于抽象数据类型的结构设计方案

在不影响性能的情况下,本方案支持数据表 示的更改,而且支持重用。此方案中,构件之间的相互作用与 模块自身紧密相关,所以,总体算法的更改或增加新功能可能 会引起现存系统的重大变更。

基于隐式调用的设计方案

对于新功能的增加尤其便利,但同样也会遇到与共享数据方 案类似的问题:对数据表示的修改和构件重用的支持不力。特别地,这种 方案可能会过分地依赖额外运行处理构件。

基于管道与过滤器的设计方案

许在处理流程中增加新过滤器,因此,一方面,该方 案支持软件重用,支持对处理算法和功能的修改。另一方面,设计数据表 示的决策将依赖于沿管道传输的数据格式。因此,根据上下游需要交换的 数据格式设计要求,可能存在额外的设计工作需要处理,如为了管理数据,需要增加解析和反解析(parsing and unparsing)数据的构件。

软件体系结构评价

软件体系结构评价是指对软件体系结构进行分析和评估,以评估其质量和可行性。

软件体系结构评价-本质上为“基于软件规格说明书的测试”这一静态测试或验证领域。

软件体系结构评价的特点-仿真模拟或者场景+走查 软件体系结构评价的可用技术-仿真+模拟或者场景+走查总之是以人的头脑来模拟计算机 核心:测试用例可以通过场景来获取。测试“执行”过程可采用人脑+场景的方式来走查。测试覆盖准则则是源于属性效用树来产生。

验证的对象是软件体系结构设计的结论 验证的目标是验证需求模型中的非功能性需求是否被软件体系结构设计结论考虑进来了、设计 效用树中的叶子节点是需要验证的各种非功能性需求,也是场景产生的依据 效用树中的一级指标是需要验证的非功能性需求 效用树中的二级指标是一级指标的进一步细化,叶子节点就是其实例

ATAM

  • 系统响应能力
  • 系统长时间运行能力
  • 系统正常运行的时间比例

敏感点 是一个或多个构件(和/或构件关系)的性质,该性质对于实现特定质量属性响应是关键的。敏感点告诉设计者 或分析师将关注点放在哪里以及何时去尝试理解一个质量目标的实现。

权衡点 权衡点是这样一种性质,该性质影响了多个属性且是 多个属性的敏感点。例如,修改加密层对于安全性和性能两 者可能会产生有意义的影响。权衡点对于设计者构造一个软 件体系结构而言,是最关键的决策。 特点: 评估SA对特定质量目标的满足情况,揭示质量目标之间的相互作用和权衡。 质量属性效用树

SAAM

专用于对SA的可修改性和功能性进行评估

ARID

适用于SA的可行性和适宜性进行测试,及对尚不完全的SA进行评估

ATAM/SAAM/ARID的比较

涉及的质量特性:

ATAM不面向任何具体质量属性。更侧重可修改性,安全性,可靠性性能,SAAM主要测试可修改性和功能。

分析的对象:

ATAM分析的对象是构架方案或样式,阐述过程,数据流,使用,物理或者模块视图的构架文档。构架文档,特别是阐述逻辑模块或者模块视图的部分。

采用的方法:

ATAM 采用效用树。SAAM利用对场景的集体讨论。

软件体系结构形式化方法

软件体系结构的形式化方法是指使用数学和形式化语言来描述、验证和分析软件体系结构.常见的软件体系结构的形式化方法包括Petri网、时序逻辑、模型检测、形式化规范和验证等

  • 基于数据共享的设计策略

以功能为基础的模块分解方式,基本上不支持软件重用。

  • 基于抽象数据类型的结构设计方案

数据将不再被所有的计算构件直接去访问(即数据不再基于共享文 件的方式,而是设计一个接口,通过该接口去访问数据,所以这 里采用的是基于抽象数据类型的方案)设计中的构件(在此为 模块)封装了数据与操作(抽象数据类型的思想),每个构件提 供相应的接口,并且每个构件管理的数据只允许通过接口规定的 方式访问(如通过过程调用方式访问)。 整个软件结构易于修改,即数据表示方式和算法设计的改变,可 在独立的构件(模块)中进行,不会对模块之间的接口产生影响;能够比方案一更好地支持软件重用。

  • 基于隐式调用的设计方案

    易于支持功能的增强,通过数据变化时所引发的调用,新增构件 (模块)能方便地增加到整个系统之中;易于处理数据修改,这是因为数据表示的修改与计算相分离; ·取决于外部事件对隐式调用模块的隐式调用策略,可支持良好的软件重用性。 隐式调用模块的处理顺序难以控制; · 采用这种数据驱动方式的模块分解,较其他方案而言,将花费更多的时间。

  • 基于管道与过滤器的设计方案「基于开源软 件开发应用的最佳选择」

    维护了一种直觉的处理流程; ·分离的过滤器对软件重用给予良好的支持; ·整个软件结构易于加入新功能(可在处理序列中加入新过滤器); ·过滤器的逻辑独立使对修改更加容易。 通过修改设计,以支持交互作用几乎是不可能的; ·空间的使用效率差,每个过滤器必须将全部数据拷贝到其输出端

软件体系结构形式化方法总结

  • 基于数据共享的面向功能的组织结构设计方案

对如下内容的支持比较弱的:总体算法改变数据表示修改(构件的)重用。 对如下内容的支持比较强:由于凭借对共享数据的直接访问, 这种方案可以获得相对来说的好性能,同时,新增加的处理功 能也相对而言比较容易访问这些共享数据。

  • 抽象数据类型方案

支持数据表 示的更改,而且支持重用.总体算法的更改或增加新功能可能 会引起现存系统的重大变更。

  • 隐式调用方案

对于新功能的增加尤其便利,但同样也会遇到与共享数据方 案类似的问题:对数据表示的修改和构件重用的支持不力。特别地,这种 方案可能会过分地依赖额外运行处理构件。

  • 管道与过滤器方案

允许在处理流程中增加新过滤器,因此,一方面,该方 案支持软件重用,支持对处理算法和功能的修改。另一方面,设计数据表 示的决策将依赖于沿管道传输的数据格式。因此,根据上下游需要交换的 数据格式设计要求,可能存在额外的设计工作需要处理,如为了管理数据,需要增加解析和反解析(parsing and unparsing)数据的构件

  • 闭环控制循环解决方案

适合于简单机器人系统,不适合于复杂机器人系

  • 分层组织模型解决方案

    由分层策略定义的抽象层提供了成功的组织构件的框架(关于每个 层的作用是精确的)。

(1)如果在实现中为详细求精而需要增 加更多的层次时,该框架就会被破坏。(2)关于机器人应该具备的通信模式

  • 基于隐式调用

更适合于复杂的机器人项目

  • 黑板模型解决方案

    黑板模型的体系结构能为任务的合作建立基础,既能 表示协同,又能以灵活的方式处理不确定性,这主要可归功于在黑板模型中采用的隐式调用机制。

Tags: 软件工程
Share: X (Twitter) Facebook LinkedIn