23のデザインパターンの紹介
クラス間の6種類の関係
デザインパターンを学ぶ前に、クラス間の 6 つの関係、つまり汎化、実装、依存関係、関連付け、集約、組み合わせが必要です。
一般化関係
一般化関係は依存関係の特殊なケースであり、2 つのクラス間の継承です。これが一般化関係です。
関係を築く
実装関係とは、インターフェイスの実装のことであり、クラスがインターフェイスを実装することを実装と呼びます。
依存関係
2 つのクラス間で相互に使用される限り、それは依存関係と呼ばれ、次のように表現できます。
- クラスは別のクラスのメンバー変数です
- クラスは別のクラスのメソッドの戻り値です
- あるクラスが別のクラスのメンバー変数である
- クラスは別のクラスのメソッドへの引数です
- あるクラスが別のクラスのローカル変数である
接続関係
アソシエーション関係はクラス間の接続であり、一方向の関係と双方向の関係があります。
集約関係
集約関係とは、全体と部分との関係を指し、全体と部分とを分離することができるという関連関係の特殊な場合である。
組み合わせ関係
結合関係とは、全体と部分との関係をいい、全体と部分とを分離することはできず、結合関係の特殊な場合でもある。
デザインパターンの原則
デザイン パターンを学習する前に、すべてのデザイン パターンが従う必要があるデザイン パターンの 6 つの原則があります。すなわち、単一責任原則、インターフェイス分離原則、依存関係反転原則、開始および終了原則、リスコフ置換原則、ディミッターの法則 (最も知られていない)原理)
単一責任の原則
簡単に言えば、単一責任とは、各クラスまたはインターフェイスが 1 つの関数だけを実行することを意味します。そのため、後で拡張するときに元のコードは変更されず、特定の関数のみを再度追加する必要があります。
インターフェース分離の原理
サブクラスによって実装されるインターフェース内に多くのメソッドがあるが、サブクラス内で使用されていないメソッドが多すぎる場合、インターフェース内のすべてのメソッドを書き直すと多くの冗長なメソッドが発生するため、インターフェース内のメソッドを分割する必要があります。より小さなインターフェースへ
依存関係逆転の原則
依存関係逆転の原理の核となる考え方はインターフェイス指向プログラミングであり、依存関係の転送には次の 3 つの方法があります。
- インターフェースの受け渡し
- コンストラクターパス
- セッターメソッドの受け渡し
開閉原理
非常に抽象的で、核となるアイデアは、拡張のためにオープンし、変更のためにクローズすることです。
リスコフ置換原理
リスコフ置換原理は結合度を下げるというもので、クラス間に継承関係ができると結合度が高くなるため、親クラスのメソッドを利用したい場合や親の機能を拡張する必要がある場合に使用します。継承メソッドは、元の親クラスをサブクラスのメンバ変数に変更するなど、クラス間の依存関係を利用して機能拡張を実現できます。
デメテル原理
最小知識の原理とも呼ばれ、直接の友達と通信するもので、クラス内にメンバ変数やメソッドのパラメータ、メソッドの戻り値として現れるクラスをフレンドと呼び、現れるクラスを友達と呼ぶと言われています。ローカル変数内の はフレンドではありません。
23種類のデザインパターン
シングルトンパターン
つまり、プロジェクト全体で作成できるインスタンス化されたオブジェクトは 1 つだけであるため、コンストラクターをプライベート化する必要があり、すべてのプログラム呼び出しをインスタンス化できるわけではなく、唯一のインスタンス化されたオブジェクトを呼び出すための外部メソッドも提供されます。
飢えた男パターンのシングルトンパターン
名前が示すように、ハングリーマンモードは、使用するかどうかに関係なく、使用するためにオブジェクトを作成し、2つのカテゴリに分けられます。
- 静的変数のハングリー モード、インスタンス化されたオブジェクトを変数に変更する
package com.njupt.single;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/08/19:15
* @Description:饿汉模式,不管你要不要,都创造出来等你用
*/
public class Hungry {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
//结果为true说明是同一个对象。
System.out.println(singleton1==singleton2);
}
}
//采用静态变量的方式实现饿汉模式
/*
* 优点:在类加载是就完成了实例化,避免了线程同步问题
* 缺点:在类加载是创建出实例化对象,如果没有使用会造成内存的浪费
* */
class Singleton{
private static Singleton singleton=new Singleton();
private Singleton(){
}
public static Singleton getSingleton(){
return singleton;
}
}
- 静的コード ブロックで記述されたシングルトン パターン
package com.njupt.single;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/08/19:15
* @Description:饿汉模式,不管你要不要,都创造出来等你用
*/
public class Hungry {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
//结果为true说明是同一个对象。
System.out.println(singleton1==singleton2);
}
}
//采用静态变量的方式实现饿汉模式
/*
* 优点:在类加载是就完成了实例化,避免了线程同步问题
* 缺点:在类加载是创建出实例化对象,如果没有使用会造成内存的浪费
* */
class Singleton{
private static Singleton singleton;
private Singleton(){
}
static{
singleton=new Singleton();}
public static Singleton getSingleton(){
return singleton;
}
}
遅延パターンのシングルトン パターン
メソッドをいつ呼び出すか、呼び出し元が使用する唯一のインスタンス化されたオブジェクトをいつ作成するかを検討します。
- スレッドアンセーフな遅延モード
package com.njupt.single;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/08/19:01
* @Description:
*/
public class Lazy {
public static void main(String[] args) {
Single single = Single.getSingle();
Single single1 = Single.getSingle();
System.out.println(single==single1);
}
}
/*优点:什么时候使用,什么时候才开始创建实例化对象
* 缺点:线程不安全的懒加载,当多个线程使用时,会造成线程安全问题
* */
class Single{
private Single(){
}
private static Single single;
public static Single getSingle(){
if(single==null){
single=new Single();
}
return single;
}
}
- 最も単純なスレッドセーフな遅延スタイルですが、効率が低すぎます。
package com.njupt.single;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/08/19:01
* @Description:
*/
public class Lazy {
public static void main(String[] args) {
Single single = Single.getSingle();
Single single1 = Single.getSingle();
System.out.println(single==single1);
}
}
/*优点:什么时候使用,什么时候才开始创建实例化对象,并且是线程安全的
* 缺点:synchronized同步代码块锁的粒度太大,性能较差效率低
* */
class Single{
private Single(){
}
private static Single single;
public static synchronized Single getSingle(){
if(single==null){
single=new Single();
}
return single;
}
}
- ダブルチェックロック
package com.njupt.single;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/08/19:01
* @Description:
*/
public class Lazy {
public static void main(String[] args) {
Single single = Single.getSingle();
Single single1 = Single.getSingle();
System.out.println(single==single1);
}
}
/*优点:什么时候使用,什么时候才开始创建实例化对象,并且是线程安全的
*推荐使用。。。
* */
class Single{
private Single(){
}
//volatile关键字禁止指令重排,因为在创建对象时有三步
/*
* 1.分配内存
* 2.初始化对象
* 3将分配的内存指向初始化对象
*
* 不加volatile会出现的问题
* 如果两个线程同时进入了同步代码块,当第一个线程抢到锁然后结束之后,
* 此时可能会先执行1.3两步,没有初始化对象,也就是此时single可能还为null,当第二个线程进入锁后
* 判断single仍然为空,此时会在创建一个对象,就不满足单例模式的要求了,因此需要volatile关键字来禁止指令重排。
*
* */
private static volatile Single single;
public static Single getSingle(){
if(single==null){
synchronized (Single.class){
if(single==null){
single=new Single();
}
}
}
return single;
}
}
- 静的内部クラスの遅延ロードは、
静的内部クラスの特性を利用します。- 外部クラスがロードされても、その静的内部クラスはすぐにはロードされません
- メソッドを呼び出すとき、静的内部クラスの変数が使用されるまで、静的内部クラスはロードされません。
package com.njupt.single;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/08/21:13
* @Description:利用静态内部类来实现懒汉模式的单例
*/
public class StaticInnerClass {
public static void main(String[] args) {
SingleLi singe = SingleLi.getSinge();
SingleLi singe2 = SingleLi.getSinge();
System.out.println(singe==singe2);
}
}
//静态内部类
class SingleLi{
//构造方法
private SingleLi(){
}
//静态内部类
private static class InnerSingle{
private static SingleLi singleLi=new SingleLi();
}
public static SingleLi getSinge(){
return InnerSingle.singleLi;
}
}
列挙を使用してシングルトン パターンを実装する
シンプルなファクトリーパターン
シンプル ファクトリ パターンは、ファクトリ パターンの一種である作成パターンです。シンプル ファクトリ パターンは、ファクトリによって作成される製品クラスのインスタンスです。シンプル ファクトリ パターンは、ファクトリ パターン ファミリの中で最も使いやすいパターンです。
中心となるアイデアは、オブジェクトを作成するクラスを定義することであり、このクラスはオブジェクトのインスタンス化の動作をカプセル化します。
ソフトウェア開発では、多数のオブジェクトを使用して特定のタイプ、特定のオブジェクトのバッチを作成する場合、ファクトリ パターンを使用します。
package com.njupt.simpleFactory;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/09/17:24
* @Description:简单工厂先出现一个抽象的父类,利用多态模式实现简单的工厂模式
*/
//先建立一个抽象的类,让其他的类来继承
public abstract class Operation {
public abstract void doMath(int i,int j);
}
class Add extends Operation{
@Override
public void doMath(int i, int j) {
System.out.println("相加之后的结果为:"+(i+j));
}
}
class Sub extends Operation{
@Override
public void doMath(int i, int j) {
System.out.println("相减之后的结果:"+(i-j));
}
}
class Multiple extends Operation{
@Override
public void doMath(int i, int j) {
System.out.println("两数相乘之后:"+(i*j));
}
}
class Div extends Operation{
@Override
public void doMath(int i, int j) {
System.out.println("两数相除之后:"+(i/j));
}
}
//简单工厂,开始处理客户端传入的数据进行处理
class Factory{
public static void startMath(int i,int j,String sym){
Operation operation;
if(sym.equals("+")){
operation=new Add();
operation.doMath(i,j);
}else if(sym.equals("-")){
operation=new Sub();
operation.doMath(i,j);
}else if(sym.equals("*")){
operation=new Multiple();
operation.doMath(i,j);
}else if(sym.equals("/")){
operation=new Div();
operation.doMath(i,j);
}else {
System.out.println("输入的符号有误!");
}
}
}
クライアントのテストコード
package com.njupt.simpleFactory;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/09/17:43
* @Description:
*/
public class OperationTest {
public static void main(String[] args) {
Factory.startMath(3,4,"+");
Factory.startMath(4,3,"-");
Factory.startMath(3,4,"*");
Factory.startMath(4,2,"/");
}
}
結果:
相加之后的结果为:7
相减之后的结果:1
两数相乘之后:12
两数相除之后:2
Process finished with exit code 0
ファクトリメソッドパターン
抽象的な工場パターン
プロトタイプパターン
プロトタイプ モードとは、プロトタイプ インスタンスを使用してオブジェクト タイプを作成し、これらのプロトタイプをコピーして新しいオブジェクトを作成することを指します。Mao は他のサルと同じになります。
動作原理は、作成されるオブジェクトにプロトタイプ オブジェクトを渡し、プロトタイプ オブジェクトに自身のコピーを要求することによって同一のオブジェクトを作成する、つまり object.clone() です。
利点: 元のオブジェクトが変更されると、他のクローン オブジェクトもそれに応じて変更されるため、コードを変更する必要がありません。
欠点: クラスごとにクローン メソッドを記述する必要がありますが、元のクラスではそれほど難しくありませんが、クローン クラスを変更する場合は、元のクラスを変更する必要があり、ソース コードを変更する必要があり、これは違反になります。 ocp原理。
クローンを作成するには、Cloneable インターフェイスを実装する必要があります
package com.njupt.prtoTypeMode;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/09/19:44
* @Description:原型模式
*/
public class Monkey implements Cloneable{
private String name;
private String color;
private String weapon;
private int age;
public Monkey() {
}
public Monkey(String name, String color, String weapon, int age) {
super();
this.name = name;
this.color = color;
this.weapon = weapon;
this.age = age;
}
@Override
public String toString() {
return "Monkey{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", weapon='" + weapon + '\'' +
", age=" + age +
'}';
}
//原型模式要重写clone方法
@Override
protected Object clone() {
Monkey monkey=null;
try {
monkey= (Monkey) super.clone();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return monkey;
}
}
Spring FrameworkのxmlでBeanタグのスコープを設定する場合、プロトタイプモードが使用されます:prototype このとき、インスタンス化されたオブジェクトをプロトタイプモードに設定できます 新しい属性がある場合も同様です元のクラス情報としてはありますが、シングルトン パターンではないため、クラスではありません。
プロトタイピングパターンの浅いコピーと深いコピー
- 浅いコピー
浅いコピー:クローン処理時に基本データ型をコピーした場合は値の転送を行い、コピーが参照データ型の場合は参照(メモリアドレス)転送、つまりコピー先の参照値を転送します。メンバー (メモリアドレス) が新しいオブジェクトにコピーされます。たとえば、Monkey King の息子属性も猿属性である場合、プロトタイプ モードでクローンを作成する場合、参照データ型にはメモリ アドレス転送 (参照転送) が使用されます。
public class Monkey implements Cloneable{
private String name;
private String color;
private String weapon;
private int age;
//当加入一个引用数据类型时:
public static Monkey son;
試験結果:
public class ProtoTest {
public static void main(String[] args) {
Monkey monkey=new Monkey("孙悟空","黄色","金箍棒",25);
monkey.son=new Monkey("孙小猴","黄色","小棍",2);
Monkey monkey1 = (Monkey) monkey.clone();
Monkey monkey2 = (Monkey) monkey.clone();
Monkey monkey3 = (Monkey) monkey.clone();
System.out.println(monkey+"引用数据类型的哈希值"+monkey.son.hashCode());
System.out.println(monkey1+"引用数据类型的哈希值"+monkey1.son.hashCode());
System.out.println(monkey2+"引用数据类型的哈希值"+monkey2.son.hashCode());
System.out.println(monkey3+"引用数据类型的哈希值"+monkey3.son.hashCode());
}
}
Monkey{
name='孙悟空', color='黄色', weapon='金箍棒', age=25}引用数据类型的哈希值764977973
Monkey{
name='孙悟空', color='黄色', weapon='金箍棒', age=25}引用数据类型的哈希值764977973
Monkey{
name='孙悟空', color='黄色', weapon='金箍棒', age=25}引用数据类型的哈希值764977973
Monkey{
name='孙悟空', color='黄色', weapon='金箍棒', age=25}引用数据类型的哈希值764977973
Process finished with exit code 0
結論:プロトタイプモードでクローンを作成する場合、基本データ型は直接割り当てられ、参照データ型のデータは参照(メモリアドレス)で渡されることがわかります。
つまり、浅いコピーはデフォルトで clone() メソッドを使用して実装されます。
2.ディープコピー
ディープコピーとは、基本データ型の場合は値をコピーし、参照データ型の場合は記憶領域に適用し、各参照データ型のメンバ変数が参照するオブジェクトをコピーし、オブジェクト全体をコピーします。コピー
実装
a. ディープコピーを実現するために clone メソッドを書き直す
package com.njupt.prtoTypeMode;
import java.io.Serializable;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/09/20:48
* @Description:孙悟空的引用数据类型的妻子
*/
public class Fox implements Cloneable, Serializable {
private String name;
private int age;
public Fox() {
}
public Fox(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Fox{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
//默认的拷贝方式是对基本数据类型进行值传递
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
孫悟空クラスのご案内
package com.njupt.prtoTypeMode;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/09/19:44
* @Description:深克隆的演示
*/
public class Monkey implements Cloneable{
private String name;
private String color;
private String weapon;
private int age;
//假设猴子有一个妻子
public Fox wife;
public Monkey son;
public Monkey() {
}
public Monkey(String name, String color, String weapon, int age) {
super();
this.name = name;
this.color = color;
this.weapon = weapon;
this.age = age;
}
@Override
public String toString() {
return "Monkey{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", weapon='" + weapon + '\'' +
", age=" + age +
'}';
}
//原型模式要重写clone方法
@Override
protected Object clone() {
//先对此类中的基本数据类型进行克隆,值传递
Monkey monkey=null;
try {
monkey= (Monkey) super.clone();
//对应用类型的属性进行单独处理
monkey.wife=(Fox) wife.clone();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return monkey;
}
}
テストコード
package com.njupt.prtoTypeMode;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/09/20:52
* @Description:
*/
public class DeepCloneTest {
public static void main(String[] args) {
Monkey monkey = new Monkey("monkey", "yellow", "bang", 25);
monkey.wife=new Fox("狐狸",24);
System.out.println(monkey+"深克隆之前的引用数据类型地址"+monkey.wife.hashCode());
Monkey m2= (Monkey)monkey.clone();
System.out.println(m2+"深克隆之后的引用数据类型地址"+m2.wife.hashCode());
}
}
テスト結果: 参照データ型の妻のアドレスが変更されていることがわかり、ディープ クローンが実行されたことがわかります。
Monkey{
name='monkey', color='yellow', weapon='bang', age=25}深克隆之前的引用数据类型地址381259350
Monkey{
name='monkey', color='yellow', weapon='bang', age=25}深克隆之后的引用数据类型地址824318946
Process finished with exit code 0
b. オブジェクトのシリアル化によるディープコピーの実現
シリアル化の特性を利用し、参照データ型を保存します。
最初に元のクラスをシリアル化し、次にそれを逆シリアル化して、参照データ型のディープ コピーを実現できます。
//将此方法加在monkey类中。
public Object deepClone(){
//先创建流对象
ByteArrayOutputStream bos=null;
ObjectOutputStream oos=null;
ByteArrayInputStream bis=null;
ObjectInputStream ois=null;
try {
//序列化
bos=new ByteArrayOutputStream();
oos=new ObjectOutputStream(bos);
//把当前对象以对象流的方式输出
oos.writeObject(this);
//反序列化
bis=new ByteArrayInputStream(bos.toByteArray());
ois=new ObjectInputStream(bis);
Monkey monkey = (Monkey) ois.readObject();
return monkey;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
テストコード
public class DeepCloneTest {
public static void main(String[] args) {
Monkey monkey = new Monkey("monkey", "yellow", "bang", 25);
monkey.wife=new Fox("狐狸",24);
System.out.println(monkey+"深克隆之前的引用数据类型地址"+monkey.wife.hashCode());
/*System.out.println(monkey+"深克隆之前的引用数据类型地址"+monkey.wife.hashCode());
Monkey m2= (Monkey)monkey.clone();
System.out.println(m2+"深克隆之后的引用数据类型地址"+m2.wife.hashCode());*/
//第二种,直接进行序列化进行深克隆
Monkey m2 = (Monkey) monkey.deepClone();
System.out.println(m2+"深克隆之后的引用数据类型地址"+m2.wife.hashCode());
}
}
結果: ディープクローン作成も実現
Monkey{
name='monkey', color='yellow', weapon='bang', age=25}深克隆之前的引用数据类型地址381259350
Monkey{
name='monkey', color='yellow', weapon='bang', age=25}深克隆之后的引用数据类型地址693632176
Process finished with exit code 0
ビルダーモード
ビルダー パターンはジェネレーター パターンとも呼ばれ、複雑なオブジェクト作成プロセスを抽象化するオブジェクト構築パターンです。この抽象化プロセスのさまざまな実装方法では、さまざまなプロパティを持つオブジェクトを構築できます。
ビルダーモードの4つの役割
- 製品 (製品の役割): 特定の製品オブジェクト
- ビルダー (抽象作成者): Product オブジェクトの各コンポーネントによって指定されるインターフェイス/抽象クラスを作成します。
- ConcreatBuilder (コンクリート クリエーター): 抽象インターフェイスまたは抽象クラスを実装し、特定の部分関数の構築と実装を担当します。
- Director: Bulider インターフェイスのオブジェクトを構築します。主に複雑なオブジェクトを作成するために使用され、ユーザーとオブジェクトの生産オブジェクトを分離する機能と、製品オブジェクトの生産プロセスを制御する機能があります。
家を建てる場合を例に挙げると、
a. 製品の役割は家であり、家はオブジェクトとしてみなされ、異なる属性を持ちます。
public class House {
private String base;
private String wall;
private String roof;}
b. 抽象作成者は家を作成するステップです。すべての家には 3 つのステップが必要です: 基礎を築く、壁を建てる、屋根を葺く、抽象クラスに変換する、さまざまなスタイルの家を書き換えるための抽象メソッドを作成する
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/10/12:03
* @Description:建造者模式
*/
public abstract class BuildModel {
//将与房子视为组合关系。
House house=new House();
abstract void buildBase();
abstract void buildWall();
abstract void buildRoof();
public House build(){
return house;
}
}
c. 特定の作成者: さまざまな家のタイプに合わせてさまざまな家を建てる方法を作成し、抽象メソッドを継承し、インターフェイスを実装します。
たとえば、普通の家を建てる場合、
package com.njupt.simpleFactory.buildModel;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/10/12:50
* @Description:
*/
public class NomalHouse extends BuildModel {
@Override
void buildBase() {
System.out.println("建造普通方子的地基2米");
}
@Override
void buildWall() {
System.out.println("建造普通房子的墙3米");
}
@Override
void buildRoof() {
System.out.println("建造普通房子的屋顶小型");
}
}
別荘を建てるように
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/10/13:46
* @Description:
*/
public class VillaHouse extends BuildModel {
@Override
void buildBase() {
System.out.println("别墅的地基3米");
}
@Override
void buildWall() {
System.out.println("别墅的墙5米");
}
@Override
void buildRoof() {
System.out.println("别墅的房顶大");
}
}
d,. 指揮官は請負業者とみなすことができ、顧客が請負業者を見つけたら、どのような方法で建てるかを指示し、要求に応じて家を建てます。
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/10/12:53
* @Description:
*/
public class Boss {
//属性是针对整个抽象方法,这样可以更加灵活,关系是聚合
private BuildModel buildModel;
public Boss(BuildModel buildModel) {
this.buildModel = buildModel;
}
public House creat(){
//建造的具体过程也应该交给包工头去建造
buildModel.buildBase();
buildModel.buildWall();
buildModel.buildRoof();
//建造好之后就可以返回建造之后的结果
return buildModel.build();
}
}
最後に、クライアントが家を建てに来るときは、請負業者を見つけて、希望する家のスタイルを伝えるだけで済みます。
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/10/10/12:53
* @Description:
*/
public class Client {
public static void main(String[] args) {
//通过构造器传入要盖多大的房子
Boss boss = new Boss(new NomalHouse());
//创建之后的结果返回给客户
House creat = boss.creat();
//再次建造一个别墅
Boss boss1=new Boss(new VillaHouse());
//创建好之后,将别墅返回给客户
House villa = boss1.creat();
}
}
注意事項:
クリエーター モードは、一般的に、共通点が多く、類似したコンポーネントを持つ製品の作成に適しています。たとえば、家を建てるには、基礎を敷設し、壁を構築し、屋根をかぶせるなどの手順が必要です。建築者により適しています。プロダクト間の差異が非常に大きい場合、ビルダー パターンの使用は適さない
抽象ファクトリ パターンとビルダー パターンの違い:
抽象ファクトリ パターンはプロダクト ファミリの作成を実現し、プロダクト ファミリは製品シリーズ。さまざまなフェレ寸法の製品ポートフォリオ
抽象的なファクトリー パターンでは、構築プロセスを知る必要はなく、製品がどの工場で生産されるかのみを考慮しますが、ビルダー パターンでは、指定された設計図に従って製品を生産する必要があり、主な目的は部品を組み立てて新しい製品を生産することです。製品。
アダプターパターン
この機能は、元々互換性のなかったインターフェイスを融合するもので、互換性への変換方法に応じて 3 つのカテゴリに分類できます。
クラスアダプター
使用シナリオ: 元のクラスは使用できず、一時的に使用するにはアダプター クラスが必要です。元のクラスはソース クラス、アダプター クラスはアダプター クラス、変換されたクラスはターゲット クラス dst クラスと呼ばれます。モードは以下を指します。 アダプター
クラス
~ src クラスを継承し、dst クラス インターフェイスを実装して、src-dst の適応を完了します。
クラスアダプタの制限:オリジナルのクラスを使用するため、オリジナルの src クラスを継承する必要がありますが、Java は単一継承機構であるため、dst クラスはインターフェイスを使用して実装する必要があります。つまり、アダプタ クラスはsrc クラスを継承して dst クラス インターフェイスを実装すると、 src–dst の適応が完了します。継承というデメリットはありますが、元のクラスを継承しているからこそ、srcクラスのメソッドを必要に応じて書き換えることができ、Adapterの柔軟性が高まります。
オブジェクトアダプター
オブジェクトアダプターの考え方はクラスアダプターと同じで、知識によってアダプタークラスが変更され、srcクラスを継承するのではなく、srcクラスをアダプタークラスに集約してdstインターフェースを実装し、src- dst適応も完了できます。
Adaptorクラスのアダプタがsrcクラスを継承しなければならない問題を解決し、継承関係を関連関係に置き換え、コストを下げ、dstをインターフェースにする必要がなくなった
インターフェースアダプターモード
使用シナリオ:インターフェイスによって提供されるすべてのデフォルト メソッドを実装する必要がない場合は、インターフェイス アダプター パターンを使用できます。これは、最初に抽象クラスを作成し、インターフェイスを実装してから、各メソッドにデフォルトの実装メソッドを提供します。インターフェイス内でクラスを抽象化します。書き換えのために親クラスの必要なメソッドを選択的にオーバーライドできます。
ブリッジモード
ブリッジ モードとは、実装の抽象化を 2 つの異なるクラス レベルに配置し、2 つのレベルが独立して構造を変更できるようにすることを指します。これは構造設計パターンです。最大の特徴は、抽象化層と実装層を分離することで、各部の独立性を保ち、機能の拡張に対応できることです。
ブリッジ モードの概略図
:
Client クラスはブリッジ モードの呼び出し元です
抽象クラス Abstraction: インプリメンタ (その実装クラスを含む) を維持します 2 つは集約関係です 抽象化はブリッジ クラスです インプリメンタはインターフェイス クラスです
動作実装クラスの
抽象クラスは集合的な関係です。
たとえば、携帯電話の操作の問題を解決するには、ブリッジ モードを使用します。直立した携帯電話を追加したい場合は、ブリッジ モードに従う方が便利です。まず、携帯電話のブランドをインターフェイスとみなし
、すべてのブランドの携帯電話が持つべき機能を抽象化したものであり、Apple Huawei およびその他のブランドはインターフェイスを実装しています
public interface Brand {
public void music();
public void play();
public void video();
}
public class Apple implements Brand {
@Override
public void music() {
System.out.println("苹果手机听音乐");
}
@Override
public void play() {
System.out.println("苹果手机玩游戏");
}
@Override
public void video() {
System.out.println("苹果手机看视频");
}
}
public class Huawei implements Brand {
@Override
public void music() {
System.out.println("华为手机听音乐");
}
@Override
public void play() {
System.out.println("华为手机玩游戏");
}
@Override
public void video() {
System.out.println("华为手机看视频");
}
}
次に、携帯電話クラスを抽象クラスとして定義し、携帯電話のブランド インターフェイス Brand を集約し、構築メソッドを通じて値を渡します。これにより、携帯電話のブランドごとに異なるブランドをインスタンス化できます。
public abstract class Phone {
Brand brand;
public Phone(Brand brand){
this.brand=brand;
}
public void play(){
brand.play();
}
public void video(){
brand.video();
}
public void music(){
brand.music();
}
}
さまざまなスタイルの携帯電話は抽象クラスを継承し、機能を実装するときに対応する携帯電話の特定の機能を書き換えます
//折叠手机的具体功能
public class FoldPhone extends Phone {
public FoldPhone(Brand brand) {
super(brand);
}
public void play(){
System.out.println("折叠的");
brand.play();
}
public void music(){
System.out.println("折叠的");
brand.music();
}
public void video(){
System.out.println("折叠的");
brand.video();
}
}
タッチスクリーン電話の特有の機能
public class Touch extends Phone {
public Touch(Brand brand) {
super(brand);
}
public void play(){
System.out.println("触屏的");
brand.play();
}
public void music(){
System.out.println("触屏的");
brand.music();
}
public void video(){
System.out.println("触屏的");
brand.video();
}
}
テスト顧客:
public class Client {
public static void main(String[] args) {
FoldPhone foldPhone = new FoldPhone(new Huawei());
foldPhone.play();
foldPhone.music();
foldPhone.video();
Touch touch = new Touch(new Huawei());
touch.music();
touch.play();
touch.video();
}
}
結果:
折叠的
华为手机玩游戏
折叠的
华为手机听音乐
折叠的
华为手机看视频
触屏的
华为手机听音乐
触屏的
华为手机玩游戏
触屏的
华为手机看视频
Process finished with exit code 0
直立型携帯電話を追加する必要がある場合は、抽象携帯電話クラスを再継承し、直立型携帯電話に固有の関数を記述するだけです。
デコレータパターン
デコレーター モード: 新しい関数をオブジェクトに動的に付加します。オブジェクト関数の拡張に関しては、オープンとクローズの原則を反映し、継承よりも柔軟です。
フライウェイトパターン
共有オブジェクト
Java では、このモードは、繰り返しオブジェクトによるメモリの浪費の問題を解決できます。要件の類似性が高い場合は、フライウェイト モードを使用して解決することを検討できます。たとえば、文字列文字列とスレッド プールの作成には、フライウェイトモード
図は以下を示しています。
- FlyWeight: 抽象的なフライウェイトの役割であり、プロダクトの抽象クラスであると同時に、オブジェクトの外部状態(変更されやすい状態、オブジェクト間で共有できない情報)と内部状態(変更が容易でない状態、オブジェクト間で共有可能な情報) インターフェイスまたは実装クラス。
- ConcreteFlyWeightは、特定のフライウェイトの役割、特定の製品カテゴリであり、抽象的な役割関連のビジネスを実現します
- UnsharedConcreteFlyweight は共有できないロールであり、通常はフライウェイト ファクトリには表示されません。
- Factory は、プール コンテナーを構築するために使用されるフライウェイト ファクトリ クラスであり、プールからオブジェクトを取得するメソッドも提供します。
コード表示
//外部状态,针对不同的使用者来使用网站
public class User {
private String name;
public User(String name){
this.name=name;
}
}
//共享的抽象类
public abstract class FlyWeight {
public abstract void use(User user);
}
//具体的实现类
public class ConcreteFlyWeight extends FlyWeight {
private String type="";
public ConcreteFlyWeight(String type){
this.type=type;
}
@Override
public void use(User user) {
System.out.println(user+"当前正在使用"+type);
}
}
//工厂类
public class flyWeightFactory {
private static HashMap<String,FlyWeight> pool=new HashMap<>();
public static FlyWeight getConcrete(String type){
if(!pool.containsKey(type)){
ConcreteFlyWeight weight = new ConcreteFlyWeight(type);
pool.put(type,weight);
}
return pool.get(type);
}
public static int poolSize(){
return pool.size();
}
}
//测试类
public class WebTest {
public static void main(String[] args) {
FlyWeight concrete = flyWeightFactory.getConcrete("知乎");
concrete.use(new User("小明"));
FlyWeight weight = flyWeightFactory.getConcrete("斗鱼");
weight.use(new User("zhangsan"));
FlyWeight weight1 = flyWeightFactory.getConcrete("斗鱼");
weight1.use(new User("历史"));
FlyWeight weight2 = flyWeightFactory.getConcrete("斗鱼");
weight2.use(new User("王维护"));
System.out.println("线程池中的对象数:"+flyWeightFactory.poolSize());
}
}
テスト結果: 4 つのオブジェクトが使用されているにもかかわらず、最終的にプール内にあるオブジェクトは 2 つだけであることがわかります。
User{
name='小明'}当前正在使用知乎
User{
name='zhangsan'}当前正在使用斗鱼
User{
name='历史'}当前正在使用斗鱼
User{
name='王维护'}当前正在使用斗鱼
线程池中的对象数:2
Process finished with exit code 0