記事ディレクトリ
序章
設計パターンは、ソフトウェア設計における一般的な問題に対処するために、特定のコンテキストで繰り返される再利用可能なソリューションです。デザインパターンをマスターすることは、よりエレガントで理解しやすく保守しやすいコードを書くのに役立つだけでなく、Java インタビューでの共通知識ポイントにもなります。この記事では、その定義、使用シナリオ、Java 実装など、いくつかの一般的な設計パターンについて説明します。
1. シングルトンモード
シングルトン パターンでは、クラスのインスタンスが 1 つだけ存在することが保証され、グローバルなアクセス ポイントが提供されます。このデザイン パターンは作成パターンであり、単一のオブジェクトのみが作成されるようにしながら、独自のオブジェクトの作成を担当する単一のクラスが関与します。このクラスは、このクラスのオブジェクトをインスタンス化せずに、その唯一のオブジェクトに直接アクセスする方法を提供します。
アプリケーション シナリオ: 頻繁にインスタンス化してから破棄する必要があるオブジェクト、データベース接続、スレッド プール、およびシステム内に長期間存在するその他のオブジェクト。
サンプルコード:
public class Singleton {
// 使用volatile关键字防止指令重排序
private static volatile Singleton instance;
private Singleton() {
}
// 提供全局访问点
public static Singleton getInstance() {
// 第一次检查
if (instance == null) {
// 加锁
synchronized (Singleton.class) {
// 第二次检查
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Singleton
クラスを作成します。クラスには、クラスのインスタンスを返すメソッドがSingleton
あります。このクラスには、他のクラスがこのクラスをインスタンス化するのを防ぐプライベート コンストラクターがあります。このメソッドは、他のクラスがこのクラスの単一インスタンスを取得する方法を提供できます。getInstance()
Singleton
getInstance()
2. ファクトリーモード
ファクトリ パターンは、オブジェクトを作成するための最適な方法を提供する創造的なデザイン パターンです。ファクトリ モードでは、オブジェクトの作成時に作成ロジックをクライアントに公開せず、新しく作成されたオブジェクトを指すために共通のインターフェイスを使用します。
アプリケーション シナリオ: コーディング時に、どのタイプのインスタンスを作成する必要があるかを予測することは不可能であるため、システムは、システムをさまざまな具体的な実装クラスから分離するためのインターフェイスを提供する必要があります。
サンプルコード:
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class ShapeFactory {
// 使用getShape方法获取形状类型的对象
public Shape getShape(String shapeType) {
if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
}
// other shape types...
return null;
}
}
まず、インターフェイスShape
と、Shape
そのインターフェイスを実装するエンティティ クラスを作成しました。次に、ファクトリー クラスを作成しますShapeFactory
。このクラスには、入力のタイプに応じてエンティティ クラスのインスタンスを返すShapeFactory
メソッドがあります。getShape
この例では、提供した情報に基づいて、ファクトリ クラスがさまざまなクラスのインスタンスを返すShapeFactory
方法を示します。
3. 抽象的なファクトリーパターン
Abstract Factory パターンは、共通のテーマを共有する個々のファクトリーのセットをカプセル化する方法を提供する創造的なデザイン パターンです。抽象ファクトリ パターンでは、抽象ファクトリは製品が何であるかを定義し、具体的なクラスを指定せずに一連の関連オブジェクトまたは相互依存オブジェクトを作成するためのインターフェイスを提供します。
アプリケーション シナリオ: システムの製品には複数の製品ファミリーがあり、システムはそのうちの 1 つの製品のみを消費します。
サンプルコード:
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
public class WinFactory implements GUIFactory {
// 返回WinButton类的实例
public Button createButton() {
return new WinButton();
}
// 返回WinCheckbox类的实例
public Checkbox createCheckbox() {
return new WinCheckbox();
}
}
ファクトリ パターンと似ていますが、今回はファクトリ クリエーター/ジェネレーター クラスという新しいレイヤーを追加しましたFactoryProducer
。AbstractFactory
class はすべてのファクトリ クラスのスーパークラスであり、FactoryProducer
渡された情報に基づいて特定のファクトリを返すことができます。
4. ビルダーモード
ビルダー パターンは、複雑なオブジェクトの構築プロセスを抽象化 (コンダクターとビルダーとして抽象化) できる創造的なデザイン パターンであり、この抽象化プロセスの異なる実装方法によって、異なるパフォーマンス (属性) を持つ複雑なオブジェクトを構築できます。 。具体的には、複雑な構造をその表現から切り離すことで、同じ構築プロセスで異なる表現を作成できるようになります。
アプリケーション シナリオ: 生成する必要がある製品オブジェクトには複雑な内部構造があり、通常、これらの製品オブジェクトには複数の部分が含まれています。
サンプルコード:
public class Pizza {
private String dough = "";
private String sauce = "";
private String topping = "";
// setters...
}
public class PizzaBuilder {
private Pizza pizza;
public PizzaBuilder() {
pizza = new Pizza();
}
public PizzaBuilder setDough(String dough) {
pizza.setDough(dough);
return this;
}
public PizzaBuilder setSauce(String sauce) {
pizza.setSauce(sauce);
return this;
}
public PizzaBuilder setTopping(String topping) {
pizza.setTopping(topping);
return this;
}
// 最终构建复杂的Pizza对象并返回
public Pizza build() {
return pizza;
}
}
Packing
およびItem
インターフェイスは食品と食品パッケージを表します。次に、これらのインターフェイスを実装するエンティティ クラス、インターフェイスを実装するBurger
エンティティ クラス、およびインターフェイスを実装するエンティティ クラスがあります。クラスは、オブジェクトを。オブジェクトの作成を担当する実際のビルダーです。ColdDrink
Item
Wrapper
Bottle
Packing
Meal
Item
MealBuilder
Meal
5. プロトタイプモード
プロトタイプ パターンは、新しいインスタンスを作成するのではなく、既存のインスタンスをコピーすることによって新しいインスタンスを返す創造的なデザイン パターンです。コピーされたインスタンスは「プロトタイプ」と呼ばれるもので、このプロトタイプはカスタマイズ可能です。
アプリケーション シナリオ: オブジェクトの作成コストは比較的高くなります (たとえば、初期化に時間がかかり、CPU リソースやネットワーク リソースが過剰に消費されます)。新しいオブジェクトは、プロトタイプ モードを通じて既存のオブジェクトをコピーすることで取得できます。類似したオブジェクトなのでコピーできます。そのメンバー変数はわずかに変更されています。
サンプルコード:
public class Prototype implements Cloneable {
// 使用 clone() 方法来创建新的实例
public Prototype clone() {
try {
return
(Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
プロトタイプ パターンでは、既存のオブジェクトを複製して新しいオブジェクトを作成します。抽象クラスShape
と、Shape
そのクラスを拡張するいくつかの具象クラスを作成します。ShapeCache
class は、シェイプ オブジェクトをハッシュテーブルに格納し、要求されたときにそのクローンを返すキャッシュ クラスです。
6. アダプターモード
アダプター パターンは、クラスのインターフェイスをクライアントが期待する別のインターフェイスに変換することで、非互換性の問題を解決するのに役立つ構造設計パターンです。このモードは主に、新しいシステムと互換性のない古いコンポーネントを再利用して、インターフェイスの互換性がないために連携できなかったクラスが連携できるようにするシナリオで使用されます。
アプリケーション シナリオ: 既存のクラス、そのメソッドが要件と異なる、つまり、インターフェイスが異なる、または既存のクラス ライブラリとあまり互換性がない可能性がある再利用可能なクラスを作成しました。どちらの側にも当てはまらない場合は、アダプター パターンを使用します。簡単に変更できます。
サンプルコード:
// 目标接口,或称为标准接口
public interface MediaPlayer {
void play(String audioType, String fileName);
}
// 适配器类,实现MediaPlayer接口
public class MediaAdapter implements MediaPlayer {
// 适配器中包含了一个需要适配的对象
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
}
}
// 调用适配器中的方法
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
MediaPlayer
インターフェイスと、MediaPlayer
インターフェイスを実装するエンティティ クラスがあります。次に、別のインターフェイスAdvancedMediaPlayer
と、AdvancedMediaPlayer
そのインターフェイスを実装するエンティティ クラスを作成します。次に、オブジェクトMediaAdapter
を使用して目的の形式を再生するアダプター クラスを作成します。AdvancedMediaPlayer
7. デコレーターモード
デコレータ パターンは、既存のオブジェクトの構造を変更せずに新しい機能を追加できる構造設計パターンです。このタイプのデザイン パターンは、継承を使用せずにオブジェクトに新しい機能を動的に追加する構造パターンです。
アプリケーションシナリオ: 多くのサブクラスを追加せずにクラスを拡張するには、オブジェクトの機能を動的に追加および取り消しする必要があります。
サンプルコード:
// 定义接口Shape
public interface Shape {
void draw();
}
// 定义装饰器DecoratorShape
public class DecoratorShape implements Shape {
protected Shape decoratedShape;
public DecoratorShape(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}
// 在装饰器中调用原始对象的方法,并添加新的功能
public void draw() {
decoratedShape.draw();
System.out.println("Additional decoration function.");
}
}
Shape
インターフェイスと、Shape
インターフェイスを実装するエンティティ クラスがあります。次に、抽象デコレータ クラスを作成しShapeDecorator
、Shape
インターフェイス。このデコレータ クラスは、装飾されたクラスをラップして新しい機能を追加します。
8. オブザーバーモード
オブザーバー パターンは、オブジェクト間の 1 対多の依存関係を定義する動作設計パターンであり、オブジェクトの状態が変化すると、関連する依存オブジェクトが通知され、自動的に更新されます。観察者パターンは行動パターンです。
アプリケーション シナリオ: オブジェクトの変更によって他のオブジェクトも同時に変更する必要があり、変更する必要があるオブジェクトの数が不明な場合は、オブザーバー モードを検討できます。
サンプルコード:
// 定义Subject,持有观察者的列表,并提供attach和notifyAllObservers方法
public class Subject {
private List<Observer> observers = new ArrayList<Observer>();
public void attach(Observer observer) {
observers.add(observer);
}
public void notifyAllObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
// 定义Observer,声明更新自己的抽象方法
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
Subject
class 、Observer
抽象クラス、およびObserver
class作成しました。Subject
オブジェクトの状態が変化すると、そのオブジェクトに依存するすべてのオブジェクトが通知され、自動的に更新されます。
9. 戦略モード
戦略パターンは、一連のアルゴリズムを定義し、各アルゴリズムをカプセル化し、それらを交換可能にする動作設計パターンです。Strategy パターンを使用すると、アルゴリズムを使用するクライアントとは独立してアルゴリズムを変更できます。
アプリケーション シナリオ: システムには多くの種類のアルゴリズムまたはビジネス ロジックがあります。これらのアルゴリズムまたはビジネス ロジックは、同じインターフェイスの異なる実装クラスにカプセル化して、複数の転送ステートメント (if...else if...else) の使用を減らすことができます。 。
サンプルコード:
// 定义策略接口,声明算法方法
public interface Strategy {
public int doOperation(int num1, int num2);
}
// 定义具体策略类
public class OperationAdd implements Strategy {
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
// 环境类,持有一个策略类的引用
public class StrategyContext {
private Strategy strategy;
public StrategyContext(Strategy strategy) {
this.strategy = strategy;
}
// 使用策略的方法
public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
ストラテジ インターフェイスStrategy
と、Strategy
そのインターフェイスを実装するエンティティ ストラテジ クラスを定義します。Context
ある戦略を使うクラスです。Context
オブジェクトは、Context
オブジェクト。
10. コマンドモード
コマンド パターンは、オブジェクト間の依存関係を簡素化し、オブジェクト間にレベルを導入することで構成と呼び出しの複雑さを軽減する動作設計パターンです。このパターンには、クライアント、呼び出し側、コマンド、ConcreteCommand、受信側の 5 つのコンポーネントが含まれます。
アプリケーション シナリオ: リクエストの呼び出し側をリクエストの受信側から切り離す必要がある場合、コマンド モードにより呼び出し側が受信側と直接対話することが防止され、呼び出し側は受信側のインターフェイスを知る必要がありません。コマンド パターンは、複数のトリガーまたはレシーバーを持ち、複数のオブジェクトにリクエストを発行する必要がある場合に使用できます。
サンプルコード:
// 命令接口,声明执行方法
public interface Order {
void execute();
}
// 具体命令类,实现Order接口的execute方法,调用接收者的方法
public class StockRequest implements Order {
private Stock stock;
public StockRequest(Stock stock) {
this.stock = stock;
}
public void execute() {
stock.buy();
}
}
// 调用者类,接收命令并执行
public class Broker {
private List<Order> orderList = new ArrayList<Order>();
public void takeOrder(Order order) {
orderList.add(order);
}
public void placeOrders() {
for (Order order : orderList) {
order.execute();
}
orderList.clear();
}
}
リクエスト クラスStock
、コマンド インターフェイスOrder
、およびOrder
インターフェイスを実装するエンティティ コマンド クラスを作成します。コマンド実装クラスはリクエストへの参照を保持し、リクエストを実行します。Broker
オブジェクトはコマンド オブジェクトとキューを使用してリクエストを実行します。
エピローグ
このブログでは、この知識が日常の開発やインタビューでの問題をより適切に解決するのに役立つことを願って、一般的に使用される 10 個の Java 設計パターンを研究しました。デザインパターンは単なるツールであることを忘れないでください。実際のプロジェクトでは、デザインパターンを無理に適用するのではなく、プロジェクトの要件や特定の条件に応じて適切なデザインパターンを選択する必要があります。