SoftwareConstruction——Reusable

2023-10-27 16:59

本文主要是介绍SoftwareConstruction——Reusable,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 可复用性的概念

programming for reuse 面向复用编程:开发出可复用的软件
programming with reuse 基于复用编程:利用已有的可复用软件搭建应用系统

优点

  • 降低成本和开发时间
  • 经过充分的测试,可靠、稳定
  • 标准化,在不同应用中保持一致

2. 面向复用的软件构造技术

Liskov Substitution Principle 里氏替换原则(LSP)

  • 子类型多态:客户端可用统一的方式处理不同类型的对象
  • 在可以使用父类的场景,都可以用子类型代替而不会有任何问题

编译强制规则

  • 子类型可以增加方法,但不可删除方法
  • 子类型需要实现抽象类型中的所有未实现方法
  • 协变:子类型中重写的方法必须有相同或子类型的返回值或者符合co-variance的参数
  • 逆变:子类型中重写的方法必须使用同样类型的参数或者符合contra-variance的参数
  • 子类型中重写的方法不能抛出额外的异常

Also applies to specified behavior (methods):

  • Same or stronger invariants 更强的不变量
  • Same or weaker preconditions 更弱的前置条件
  • Same or stronger postconditions 更强的后置条件

协变 & 反协变

父类型 → 子类型:

  • 协变:返回值和异常不变或越来越具体
  • 逆变(反协变):参数类型要相反地变化,要不变或越来越抽象

泛型
泛型类型是不支持协变的:

ArrayList<String>List<String>的子类型,但List<String>不是List<Object>的子类型

这是因为发生了类型擦除,运行时就不存在泛型了,所有的泛型都被替换为具体的类型。

但是在实际使用的过程中是存在能够处理不同的类型的泛型的需求的,如定义一个方法参数是List<E>类型的,但是要适应不同的类型的E,于是可使用通配符?来解决这个需求:

无类型条件限制:

public static void printList(List<?> list) {for (Object elem: list)System.out.print(elem + " ");
}

当为A类型的父类型

public static void printList(List<? super A> list){...}

当为A类型的子类型

public static void printList(List<? extends A> list){...}

委派(Delegation)
一个对象请求另一个对象的功能
通过运行时动态绑定,实现对其他类中代码的动态复用

“委托”发生在object层面
“继承”发生在class层面

Types of Delegation:

依赖 Dependency:临时性的delegation

  • 把被delegation的对象以参数方式传入。只有在需要的时候才建立与被委派类的联系,而当方法结束的时候这种关系也就随之断开了。
class Duck {//no field to keep Flyable objectpublic void fly(Flyable f) { f.fly(); } //让这个鸭子以f的方式飞public void quack(Quackable q) { q.quack() }; //让鸭子以q的方式叫
}

关联 Association:永久性的delegation

  • 分为:组合(Composition)聚合(Aggregation)
  • 被delegation的对象保存在rep中,该对象的类型被永久的与此ADT绑定在了一起。

组合 Composition:更强的Association👆,但难以变化

  • 在rep或构造方法中设定
Duck d = new Duck();
d.fly();
class Duck {//这两种实现方式的效果是相同的Flyable f = new FlyWithWings(); //写死在rep中public Duck() { f = new FlyWithWings(); } //写死在构造方法中public void fly(){ f.fly(); }
}

聚合 Aggregation:更弱的Association👆,可动态变化

  • 在构造方法中传入参数绑定
Flyable f = new FlyWithWings();
Duck d = new Duck(f);
d.fly();
class Duck {Flyable f; // 这个必须由构造方法传入参数绑定public Duck(Flyable f) { this.f = f; } // 在此传入public void fly(){ f.fly(); }
}

组合(Composition)和CRP原则

  • 利用delegation的机制,将功能的具体实现与调用分离,在实现中又通过接口的继承树实现功能的不同实现方法,而在调用类中只需要创建具体的子类型然后调用即可。组合就是多个不同方面的delegation的结合。

  • 抽象层是不会轻易发生变化的,会发生变化的只有底层的具体的子类型,而具体功能的变化(实现不同的功能)也是在最底层,所以抽象层是稳定的。而在具体层,两个子类之间的委派关系就有可能是稳定的也有可能是动态的,这取决于需求和设计者的设计决策。


白盒框架 & 黑盒框架的原理与实现

黑盒框架

  • 通过实现特定接口进行框架扩展,采用的是delegation机制达到这种目的,通常采用的设计模式是策略模式(Strategy)和观察者模式(Observer);
  • 黑盒所预留的是一个接口,在框架中只调用接口中的方法,而接口中方法的实现就依据派生出的子类型的不同而不同,它的客户端启动的就是框架本身。

白盒框架

  • 通过继承和重写实现功能的扩展,通常的设计模式是模板模式(Template Method);
  • 白盒框架所执行的是框架所写好的代码,只有通过override其方法来实现新的功能,客户端启动的的是第三方开发者派生的子类型。

3. 面向复用的设计模式

Adapter 适配器模式

将某个类/接口转换为client期望的其他形式
增加接口

  • 通过增加一个接口,将已存在的子类封装起来
  • client面向接口编程,从而隐藏了具体子类。

适用场合:你已经有了一个类,但其方法与目前client的需求不一致。
根据OCP原则,不能改这个类,所以扩展一个adaptor和一个统一接口。

Decorator 装饰者模式

继承组合会引起组合爆炸/代码重复

  • 为对象增加不同侧面的特性
  • 对每一个特性构造子类,通过委派机制增加到对象上
  • 客户端需要一个具有多种特性的object,通过逐层的装饰来实现

例子:

  • Stack对应上图Component接口
  • ArrayStack对应ConcreteComponent,基础类
  • StackDecorator对应Decorator,装饰类(可以是抽象类)
  • UndoStack对应ConcreteDecoratorA,装饰类的具体类
//Stack接口,定义了所有的Stack共性的基础的功能
interface Stack {void push(Item e);Item pop();
}
//最基础的类,啥个性也没有的Stack,只有共性的实现
public class ArrayStack implements Stack {... //reppublic ArrayStack() {...}public void push(Item e) {...}public Item pop() { ... }
}
//装饰器类,可以是一个抽象类,用于扩展出有各个特性方面的各个子类
public abstract class StackDecorator implements Stack {protected final Stack stack; //用来保存delegation关系的reppublic StackDecorator(Stack stack) {this.stack = stack; //建立稳定的delegation关系}public void push(Item e) {stack.push(e); //通过delegation完成任务}public Item pop() {return stack.pop(); //通过delegation完成任务}
}
//一个有撤销特性功能的子类
public class UndoStack extends StackDecorator implements Stack {private final UndoLog log = new UndoLog();public UndoStack(Stack stack) {super(stack); //调用父类的构造方法建立delegation关系}public void push(Item e) {log.append(UndoLog.PUSH, e); //实现个性化的功能super.push(e); //共性的功能通过调用父类的实现来完成}public void undo() {//implement decorator behaviors on stack}...
}
  • 使用装饰对象:层层嵌套初始化new Class1(new Class2(new Class3(...)))
// 先创建出一个基础类对象
Stack s = new ArrayStack();
// 利用UndoStack中继承到的自己到自己的委派建立起从UndoStack到ArrayStack的delegation关系
// 这样,UndoStack也就能够实现最基础的功能,并且自身也实现了个性化的功能
Stack us = new UndoStack(s);
// 通过一层层的装饰实现各个维度的不同功能
Stack ss = new SecureStack(new SynchronizedStack(us));

JDK中装饰器模式的应用:
static List<T> unmodifiableList(List<T> list)
static Set<T> synchronizedSet(Set<T> set)

facade 外观模式

  • 客户端需要通过一个简化的接口来访问复杂系统内的功能
  • 提供一个统一的接口来取代一系列小接口调用,相当于对复杂系统做了一个封装,简化客户端使用
  • 便于客户端学习使用,解耦


Strategy 策略模式

  • 有多种不同的算法来实现同一个任务
  • 但需要client根据需要动态切换算法,而不是写死在代码里
  • 为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例

Template Method 模板方法模式

  • 框架:白盒框架
  • 做事情的步骤一样,但具体方法不同
  • 共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现
  • 使用继承和重写实现模板模式

适用场合:有共性的算法流程,但算法各步骤有不同的实现典型的“将共性提升至超类型,将个性保留在子类型”

Iterator 迭代器模式

客户端希望遍历被放入容器/集合类的一组ADT对象,无需关心容器的具体类型
也就是说,不管对象被放进哪里,都应该提供同样的遍历方式

实现方式是在ADT类中实现Iterable接口,该接口内部只有一个返回一个迭代器的方法,然后创建一个迭代器类实现Iterator接口,实现hasnext()、next()、remove()这三个方法。
 

这篇关于SoftwareConstruction——Reusable的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/287070

相关文章

Design Patterns - Elements of Reusable Object-Oriented Software

《Design Patterns - Elements of Reusable Object-Oriented Software》 中文译名:设计模式 - 可复用的面向对象软件元素 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版;四位作者合称 GOF(四人帮,全拼 Gang of Four)。 他

深入鸿蒙开发-如何使用好 ArkUI 的 @Reusable?

如何使用好 ArkUI 的 @Reusable? OpenHarmony 组件复用机制 在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为 系统组件,由开发者定义的称为 自定义组件。 在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。

UVM_USERS_GUIDE_Developing Reusable Verification Components

3. Developing Reusable Verification Components 本章描述了构成典型验证环境的基本概念和组件。它还展示了如何使用经过验证的层次结构组合这些组件来创建可重用的验证组件。 —Modeling Data Items for Generation — Transaction-Level Components — Creating the Driver — C

JBoss 系列三十八:jBPM5示例之 Reusable Sub-Process

jBPM5中的Reusable Sub-Process是在主流程里面执行另为一个流程(子流程),当流程执行到Reusable Sub-Process节点时jBPM5流程执行引擎根据提供的流程(子流程)ID,Reusable Sub-Process示例流程如下(主流程和子流程): 主流程 子流程 Reusable Sub-Process示例流程运行时传入三条字符串message 1

持续学习-Towards reusable network components by learning compatible representations-arxiv2020

Abstract This paper proposed to make a first step towards compatible and hence reusable network components. Split a network into two components: a feature extractor and a target task head. 最终验证在三个应

Visitor(Reference:Design Patterns - Elements of Reusable Object-Oriented Software)

Visitor --- 为对象结构中ConcreteElement的每一个类声明一个Visit操作!该操作的名字和特征标识了发送Visit请求给该访问者的类。使得访问者可以确定正在被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。 ConcreteVisitor --- 实现每个由Visitor声明的操作。每个操作实现本算法的一部分,该算法是对应于结构中对象的类。Conc

Iterator(Reference:Design Patterns - Elements of Reusable Object-Oriented Software)

Iterator --- 迭代器定义访问和遍历元素的接口。 ConcreteIterator --- 具体迭代器实现迭代器接口 --- 对该聚合遍历时跟踪当前位置。 Aggregate --- 聚合定义创建相应迭代器对象的接口。 ConcreteAggregate --- 具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。

Composite(Reference:Design Patterns - Elements of Reusable Object-Oriented Software)

Component --- 为组合中的对象声明接口 --- 在适当的情况下,实现所有类共有接口的缺省行为 --- 声明一个接口用于访问和管理Component的子组件 --- 在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它 Leaf --- 在组合中表示叶节点对象,叶节点没有子节点 --- 在组合中定义图元对象的行为 Composite --- 定义有子部件

State(Reference:Design Patterns - Elements of Reusable Object-Oriented Software)

Context --- 定义客户感兴趣的接口 --- 维护一个ConcreteState子类的实例,这个实例定义当前状态。 State --- 定义一个接口以封装与Context的一个特定状态相关的行为 ConcreteState subclasses --- 每一子类实现一个与Context的一个状态相关的行为

Singleton(Reference:Design Patterns - Elements of Reusable Object-Oriented Software)

Singleton --- 定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作 --- 可能负责创建它自己的唯一实例。