デコレーターパターンの紹介
デコレータ パターンの本来の定義は、オブジェクトに追加の責任を動的に追加することですが、機能の拡張という点では、デコレータ パターンはサブクラスを使用するより柔軟な代替手段を提供します。
今、ケーキがあるとします。クリームだけでコーティングされていれば、このケーキは通常のクリームケーキであり、ブルーベリーを追加すると、このケーキはブルーベリーケーキになります。別のダークチョコレートを取り出して、名前を書いて年齢のキャンドルを入れるとバースデーケーキになります
ソフトウェア設計において、デコレータ パターンは継承を置き換えるために使用されるテクノロジであり、サブクラスを定義する必要がない方法でオブジェクトに責任を動的に追加し、オブジェクト間の関連関係を使用してクラス間の継承関係を置き換えます。
デコレータパターンの原理
デコレータ モードでの役割:
-
抽象構造 (コンポーネント) の役割: 具体構造と抽象装飾クラスの共通の親クラスであり、具体構造に実装されるビジネス メソッドを宣言します。クライアントが装飾されていないオブジェクトと装飾を一貫した方法で処理できるようにします。オブジェクト、クライアントの透過的な動作を実現
-
具体コンポーネント (具体コンポーネント) の役割: 抽象構造クラスのサブクラスであり、特定の構造オブジェクトを定義し、抽象構造で宣言されたメソッドを実装するために使用されます。装飾クラスは、それに追加の責任 (メソッド) を追加できます。
-
抽象装飾 (デコレーター) 役割: これは、特定の構造に責任を追加するために使用される抽象構造クラスのサブクラスでもありますが、特定の責任はそのサブクラスに実装されます。これは、抽象構造オブジェクトへの参照を維持します。これは、オブジェクトを構築するメソッドを装飾する前に呼び出すことができ、そのサブクラスを通じてメソッドを拡張して装飾の目的を達成します。
-
具象デコレータ (ConcreteDecorator) の役割: 抽象デコレータ クラスのサブクラスであり、構造に新しい責任を追加します。各具象デコレータ クラスはいくつかの新しい動作を定義し、抽象デコレータ クラスで定義されたメソッドと新しいメソッドを呼び出すことができます。を追加してオブジェクトの動作を拡張できます。
コードは以下のように表示されます:
/**
* 抽象构建类
*
**/
public abstract class Component {
//抽象方法
public abstract void operation();
}
/**
* 具体构建类
*
**/
public class ConcreteComponent extends Component {
@Override
public void operation() {
//基础功能实现(复杂功能通过装饰类进行扩展)
}
}
/**
* 抽象装饰类-装饰者模式的核心
**/
public class Decorator extends Component{
//维持一个对抽象构建对象的引用
private Component component;
//注入一个抽象构建类型的对象
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
//调用原有业务方法(这里并没有真正实施装饰,而是提供了一个统一的接口,将装饰过程交给子类完成)
component.operation();
}
}
/**
* 具体装饰类
**/
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation(); //调用原有业务方法
addedBehavior(); //调用新增业务方法
}
//新增业务方法
public void addedBehavior(){
//......
}
}
デコレータパターンの応用例
デコレーター モードの使用法を示すためにファイル リーダー プログラムを例に挙げてみましょう。以下はプログラムの UML クラス図です。
-
データローダー
-
抽象ファイル読み込みインターフェース DataLoader
-
-
BaseFileDataLoader
-
特定のコンポーネントBaseFileDataLoader、コンポーネントDataLoaderの読み取りおよび書き込みメソッドを書き換えます。
-
-
データローダーデコレータ
-
デコレータ DataLoaderDecorator には、DataLoader を参照するオブジェクト インスタンス ラッパーが含まれている必要があります。これは DataLoader メソッドも書き換えますが、ここではラッパーを読み取りと書き込みに使用し、展開しません。
-
-
暗号化データデコレータ
-
読み取りおよび書き込み時の暗号化および復号化機能を備えた特定のデコレータ EncryptionDataDecorator は、読み取りおよび書き込みメソッドを書き換えるためのデコレータ DataLoaderDecorator を継承します。
-
IOツールクラスをインポートする
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
1 ) データローダー
/**
* 抽象的文件读取接口DataLoader
**/
public interface DataLoader {
String read();
void write(String data);
}
2 ) BaseFileDataLoader
/**
* 具体组件,重写读写方法
**/
public class BaseFileDataLoader implements DataLoader {
private String filePath;
public BaseFileDataLoader(String filePath) {
this.filePath = filePath;
}
@Override
public String read() {
try {
String result = FileUtils.readFileToString(new File(filePath), "utf-8");
return result;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
public void write(String data) {
try{
FileUtils.writeStringToFile(new File(filePath), data, "utf-8");
} catch (IOException e) {
e.printStackTrace();
}
}
}
3 ) データローダーデコレータ
/**
* 抽象装饰者类
**/
public class DataLoaderDecorator implements DataLoader {
private DataLoader wrapper;
public DataLoaderDecorator(DataLoader wrapper) {
this.wrapper = wrapper;
}
@Override
public String read() {
return wrapper.read();
}
@Override
public void write(String data) {
wrapper.write(data);
}
}
4 ) 暗号化データデコレータ
/**
* 具体装饰者-对文件内容进行加密和解密
**/
public class EncryptionDataDecorator extends DataLoaderDecorator {
public EncryptionDataDecorator(DataLoader wrapper) {
super(wrapper);
}
@Override
public String read() {
return decode(super.read());
}
@Override
public void write(String data) {
super.write(encode(data));
}
//加密操作
private String encode(String data) {
try {
Base64.Encoder encoder = Base64.getEncoder();
byte[] bytes = data.getBytes("UTF-8");
String result = encoder.encodeToString(bytes);
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//解密
private String decode(String data) {
try {
Base64.Decoder decoder = Base64.getDecoder();
String result = new String(decoder.decode(data), "UTF-8");
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
5) テスト
public class TestDecorator {
public static void main(String[] args) {
String info = "name:tom,age:15";
DataLoaderDecorator decorator = new EncryptionDataDecorator(new BaseFileDataLoader("demo.txt"));
decorator.write(info);
String data = decorator.read();
System.out.println(data);
}
}
デコレータパターンまとめ
デコレータ パターンの利点:
-
オブジェクトの機能を拡張する場合、装飾モードは継承よりも柔軟であり、クラス数の急激な増加を引き起こしません。
-
オブジェクトの機能は動的に拡張でき、実行時に構成ファイルを通じてさまざまな特定の装飾クラスを選択して、さまざまな動作を実現できます。
-
オブジェクトは複数回装飾できます。さまざまな特定の装飾クラスと、これらの装飾クラスの配置と組み合わせを使用することにより、さまざまな動作の多くの組み合わせを作成して、より強力なオブジェクトを取得できます。
-
特定構築クラスと特定装飾クラスは独立して変更可能 ユーザーは必要に応じて新しい特定構築クラスと特定装飾クラスを追加可能 元のクラスライブラリのコードは無秩序に変更され、オープンとクローズの原則に準拠。
デコレータ パターンの欠点:
-
システム設計でデコレーション モードを使用すると、多数の小さなオブジェクトが生成されます。これらのオブジェクトの違いは、クラスや属性値の違いではなく、オブジェクト間の接続方法にあります。多数の小さなオブジェクトが生成されると、システム リソースが増えると、プログラムのパフォーマンスにある程度の影響が生じます。
-
デコレーター モードは、継承よりも柔軟で柔軟なソリューションを提供しますが、継承よりもエラーが発生しやすく、デバッグがより困難であることも意味します。複数回装飾されたオブジェクトの場合は、段階的にデバッグしてエラーを見つける必要がある場合があります。チェックはさらに面倒です。
デコレータ パターンの適用可能なシナリオ
-
クラスの機能シナリオを迅速かつ動的に拡張および取り消します。たとえば、API インターフェイスのセキュリティ要件が高い一部のシナリオでは、デコレーション モードを使用して、送信される文字列データを圧縮または暗号化できます。セキュリティ要件が高くない場合は使用できません。
-
拡張クラスを継承するシナリオはサポートされていません。たとえば、final キーワードを使用したクラスや、システム内の継承によって生成された多数のサブクラスなどです。