问候,
上周,我们讨论了Visitor设计模式。 这星期我们
谈论一些有时需要的附加功能,即
该功能是可选的。 假设有很多可选功能
人们想要的。
本文讨论了装饰器(或“包装器”)模式。 为了
在该示例中,我们将使用数组操作。 人们总是摆弄着
数组,即它们将值从一个数组复制到另一个数组,从而在元素周围移动
在数组等中,就像没有面向对象的编程一样。
从Fortran的早期开始,人们就开始使用数组
并破坏那些必须一遍又一遍地复制数据的不良处理器
每时每刻。
让我们对此做些事情:以下接口是的抽象
数组:
public interface Sequence<T> {
public int length();
public T get(int idx);
public void set(T, int idx);
}
序列有一个长度,我们可以获取和设置序列中的元素;
没有
壮观。 请注意,此接口是通用接口:Sequence
操作“ T”类型的元素。
让我们封装一个简单的数组:
public class ArraySequence<T> implements Sequence<T> {
// the encapsulated array:
private T[] array;
// the constructor:
public ArraySequence(T[] array) { this.array= array; }
// interface implementation:
public int length() { return array.length; }
public T get(int idx) { return array[idx]; }
public void set(T elem, int idx) { array[idx]= elem; }
}
为什么要为一个简单的数组构建所有代码?
我们本可以使用数组
本身是第一位的,那么为什么所有这些复杂的东西呢? 这就是为什么:
有时我们想操纵数组,但是我们不想复制和移动
并一遍又一遍地交换所有这些元素:我们为此构建了装饰器
代替。 ArraySequence类没有什么特别的:它只是封装
我们的数组。 让我们构建一个抽象类,从中可以轻松构建
其他具体的Sequence实现:
public abstract class AbstractSequence<T> implements Sequence<T> {
// another sequence:
protected Sequence<T> seq;
// an abstract method that gives us an index value:
protected abstract int index(int idx);
// the constructor:
public AbstractSequence(Sequence<T> seq) { this.seq= seq; }
// interface implementation:
public int length() { return seq.length(); }
public T get(int idx) { return seq.get(index(idx)); }
public void set(T elem, int idx) { seq.set(elem, index(idx)); }
}
类本身不能做任何事情(它是抽象的),但是它给了我们所有人
具体序列实现所需的功能。 以下
序列以相反的顺序处理序列的所有元素:
public class ReverseSequence<T> extends AbstractSequence<T> {
// implementation of index() method:
protected int index(int idx) { return seq.length()-idx-1; }
// the constructor:
public ReverseSequence(Sequence<T> seq) { super(seq); }
}
您不会在此类中看到任何接口实现,因为所有内容已经
由AbstractSequence实现; 该类实现的全部是
index()方法可提供其他功能。 请注意
封装的“ seq”序列将传递给构造函数中的超类。
如果您创建一个ReverseSequence,则在给定另一个Sequence的情况下,您可以
其他序列,就好像它被颠倒了(即最后一个元素在前,反之亦然)。
添加的功能在index()方法中实现。 有点
展示如何使用的代码:
Integer[] a= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
Sequence rev= new ReverseSequence<Integer>(
new ArraySequence<Integer>(a));
...
for (int i= 0, n= rev.length(); i < n; i++)
System.out.print(rev.get(i)+" ");
System.out.println();
输出为:9 8 7 6 5 4 3 2 1 0
从上面的代码片段中,您可以看到为什么装饰器也被命名为包装器:
Sequence对象“包装”其他Sequence对象(传递给构造函数)。
还要注意上面的代码片段,我们的代码没有直接接触数组
将其包装在Sequence中后,
这是另一个Sequence实现:将第一个元素放入
其他序列的一部分与最后一部分的元素交织
的顺序:
public class MergeSequence<T> extends AbstractSequence<T> {
// implementation of index() method:
protected int index(int idx) {
if ((idx&1) == 0) return idx/2; // idx is even
return (seq.length()+idx)/2; // idx is odd
}
// the constructor:
public MergeSequence(Sequence<T> seq) { super(seq); }
}
如果您使用上面的代码段,但使用MergeSequence而不是
ReverseSequence的输出将是:0 5 1 6 2 7 3 8 4 9。
您将原始数组包装在两个
MergeSequence和ReverseSequence如下所示:
Sequence magic= new MergeSequence<Integer>(
new ReverseSequence<Integer>(
new ArraySequence<Integer>(a)));
我还要由您自己决定,是否可以
您改用了此换行顺序:
Sequence magic= new ReverseSequence<Integer>(
new MergeSequence<Integer>(
new ArraySequence<Integer>(a)));
请注意,如果您想使用一个简单的方法来完成这两个包装程序的操作
数组,您必须玩一些巧妙的技巧和/或使用临时数组来保存
中间结果。
这是一个更复杂的序列:它连接另外两个序列
如果它们只是一个更大的序列。 我们确实有一些工作要做
该类的接口方法的实现,所以我们不
从AbstractSequence扩展。 开始:
public class CatenateSequence<T> implements Sequence<T> {
// the first and second Sequence:
private Sequence<T> first, second;
// little helper method:
private boolean isFirst(int idx) { return idx < first.length(); }
// implementation of index() method:
private int index(int idx) {
if (isFirst(idx)) return idx;
return idx-first.length();
}
// the constructor:
public CatenateSequence(Sequence<T> first, Sequence<T> second) {
this.first = first;
this.second= second;
}
// interface implementation:
public int length() { return first.length()+second.length(); }
public T get(int idx) {
if (isFirst(idx)) return first.get(index(idx));
return second.get(index(idx));
}
public void set(T elem, int idx) {
if (isFirst(idx)) first.set(elem, index(idx));
else second.set(elem, index(idx));
}
}
最后一段代码展示了如何将三个数组连接在一起
使用CatenateSequence包装器:
Integer[] a= { 0, 1, 2 };
Integer[] b= { 3, 4, 5 };
Integer[] c= { 6, 7, 8, 9 };
...
Sequence cat= new CatenateSequence<Integer>(
new ArraySequence<Integer>(a),
new CatenateSequence<Integer>(
new ArraySequence<Integer>(b),
new ArraySequence<Integer>(c)));
如果您打印此序列,输出将再次为:0 1 2 3 4 5 6 7 8 9。
请注意,我也偷偷地为CatenateSequence使用了另一种设计模式
class:复合模式。 但是,我们将在下一个技巧中进行讨论。
猜猜如果将CatenateSequence包装在另一个包装中会造成多大的混乱
再次排序。 请注意,没有单个数组元素被移动或复制。
我们通过将一个序列包装在另一个序列中来随意添加功能
为我们实现了功能。 我们可以将序列包装在任何
我们想要的订单,因此我们可以完成我们想要的任何功能。 我们现在
分为三类:
1:逆序
2:MergeSequence
3:CatenateSequence
如果我们使用单独的工具实现了所有这些功能中的任何一个
班级,我们本来会有很多班级。 现在我们只
有三个,我们可以按照我们想要的任何顺序进行组合
如我们所愿。 (我在计数中排除了ArraySequence类,因为我们
总是需要它来封装原始的简单数组)。
Java核心类中的Readers,Writer,InputStreams和OutputStreams
以类似的方式实现:您包装(或“装饰”)字节或字符
流使用所有这些包装器。 每个包装器都会添加一些功能,如果
你需要它。
下次我们再谈谈为什么以及何时可以使用继承或不使用继承
实现(从另一个扩展一个类)。 结果可能是
时不时地令人惊讶。
亲切的问候,
乔斯
From: https://bytes.com/topic/java/insights/639480-patterns-decorator-pattern