模式:装饰器模式

问候,

上周,我们讨论了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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值