Decorator模式(也称为Wrapper )允许将行为静态或动态地添加到单个对象,而不会影响同一类中其他对象的行为。 可以将其视为子类的替代方法。 我们知道子类在编译时会增加行为,并且更改会影响原始类的所有实例。 另一方面,装饰可以在运行时为选择性对象提供新的行为。
装饰器与其装饰的组件的接口一致,从而对组件的客户端透明。 装饰器将请求转发到组件,并且可以在转发之前或之后执行其他操作。 透明度允许装饰器递归嵌套,从而允许无限数量的添加职责。 装饰器模式的主要参与者如下所示:
- 组件 –指定可以动态添加职责的对象的接口。
- ConcreteComponent –定义可以添加其他职责的对象
- 装饰器 –保留对Component对象的引用并符合Component的接口。 它包含要装饰的Component对象。
- ConcreteDecorator –向组件添加责任。
现在,让我们来看一个装饰器模式的具体示例,并看一下如何使用lambda表达式对其进行转换。 假设我们有不同类型的书籍,而这些书籍的封面或类别可能不同。 我们可以选择任何书籍,并通过继承来指定类别或语言。 可以将书籍抽象为一类。 之后,任何其他类都可以扩展Book类并覆盖cover或category的方法。 但这不是一种有效的方法。 在这种方法下,子类可能具有从超类扩展过来的不必要的方法。 同样,如果我们必须添加更多的属性或特性,则父类也会有所变化。 更改类的实现应该是最后的选择。
让我们通过使用装饰器模式来采取最佳方法。 我们将使用基本方法为Book创建一个接口:
public interface Book {
public String describe();
}
BasicBook类可以实现此接口以提供最小的抽象:
public class BasicBook implements Book {
@Override
public String describe() {
return "Book";
}
}
接下来,让我们定义抽象类BookDecorator ,它将充当Decorator:
abstract class BookDecorator implements Book {
protected Book book;
BookDecorator(Book book) {
this.book = book;
}
@Override
public String describe() {
return book.describe();
}
}
BookDecorator类符合Book接口,并且还存储对Book接口的引用。 如果要将类别作为属性添加到Book接口,则可以使用实现BookDecorator接口的具体类。 对于小说类别,我们可以使用以下装饰器:
public class FictionBookDecorator extends BookDecorator {
FictionBookDecorator(Book book) {
super(book);
}
@Override
public String describe() {
return ("Fiction " + super.describe());
}
}
您可以看到FictionBookDecorator在原始操作(即描述)中添加了图书的类别。 同样,如果要指定“科学”类别,则可以具有相应的“ ScienceBookDecorator” :
public class ScienceBookDecorator extends BookDecorator {
ScienceBookDecorator(Book book) {
super(book);
}
@Override
public String describe() {
return ("Science " + super.describe());
}
}
ScienceBookDecorator还会在原始操作中添加书籍的类别。 也可以有另一套装饰器,指示书籍的封面类型。 我们可以用SoftCoverDecorator来描述这本书有一个软封面。
public class SoftCoverDecorator extends BookDecorator {
SoftCoverDecorator(Book book) {
super(book);
}
@Override
public String describe() {
return (super.describe() + " with Soft Cover");
}
}
我们还可以使用HardCoverDecorator来描述这本书的精装本。
public class HardCoverDecorator extends BookDecorator {
HardCoverDecorator(Book book) {
super(book);
}
@Override
public String describe() {
return (super.describe() + " with Hard Cover");
}
}
现在,让我们结合定义的所有类和接口,以利用Decorator模式的强大功能。 仅查看所有这些类的一个示例相互作用:
import java.util.List;
import java.util.ArrayList;
public class BookDescriptionMain {
public static void main(String [] args) {
BasicBook book = new BasicBook();
//Specify book as Fiction category
FictionBookDecorator fictionBook = new FictionBookDecorator(book);
//Specify that the book has a hard cover
HardCoverDecorator hardCoverBook = new HardCoverDecorator(book);
//What if we want to specify both the category and cover type together
HardCoverDecorator hardCoverFictionBook = new HardCoverDecorator(fictionBook);
//Specify book as Science category
ScienceBookDecorator scienceBook = new ScienceBookDecorator(book);
//What if we want to specify both the category and cover type together
HardCoverDecorator hardCoverScienceBook = new HardCoverDecorator(scienceBook);
//Add all the decorated book items in a list
List<Book> bookList = new ArrayList<Book>() {
{
add(book);
add(fictionBook);
add(hardCoverBook);
add(hardCoverFictionBook);
add(scienceBook);
add(hardCoverScienceBook);
}
};
//Traverse the list to access all the book items
for(Book b: bookList) {
System.out.println(b.describe());
}
}
}
运行此命令可获得以下输出:
Book
Fiction Book
Book with Hard Cover
Fiction Book with Hard Cover
Science Book
Science Book with Hard Cover
它清楚地说明了如何将不同的属性添加到任何预定义的类/对象。 同样,可以组合多个属性。 我试图将所有装饰的书本组合在一个列表中,然后通过遍历该列表来访问它们。
到目前为止,我们所看到的只是标准的装饰器模式,而且已经存在了很长时间。 在这些时候,当函数式编程成为新的流行语时,人们可能会思考Java中对lambda表达式的支持是否会有所不同。 实际上,由于修饰后的接口就像一个函数接口,因此我们可以使用Java中的lambda表达式进行哈希处理。 让我们看一下代码的样子:
import java.util.List;
import java.util.ArrayList;
public class BookDescriptionMainWithLambda {
public static void main(String [] args) {
BasicBook book = new BasicBook();
//Specify book as Fiction category using Lambda expression
Book fictionBook = () -> "Fiction " + book.describe();
//Specify that the book has a hard cover using Lambda expression
Book hardCoverBook = () -> book.describe() + " with Hard Cover";
//What if we want to specify both the category and cover type together
Book hardCoverFictionBook = () -> fictionBook.describe() + " with Hard Cover";
//Specify book as Science category using Lambda expression
Book scienceBook = () -> "Science " + book.describe();
//What if we want to specify both the category and cover type together
Book hardCoverScienceBook = () -> fictionBook.describe() + " with Hard Cover";
List<Book> bookList = new ArrayList<Book>() {
{
add(book);
add(fictionBook);
add(hardCoverBook);
add(hardCoverFictionBook);
add(scienceBook);
add(hardCoverScienceBook);
}
};
bookList.forEach(b -> System.out.println(b.describe()));
}
}
运行此命令可获得类似的输出:
Book
Fiction Book
Book with Hard Cover
Fiction Book with Hard Cover
Science Book
Fiction Book with Hard Cover
我们可以看到,使用lambda表达式会使装饰器的其他类变得多余。 您不需要其他课程; 只需使用lambda表达式指定其他行为。 但是,支持再次找到装饰器以供重新使用。 如果您有具体的装饰器类,则下次也可以重用它。
- 可以从我的github存储库访问所有代码片段