JAvaのいくつかのよく使われる設計モード

44949 ワード

今日はいくつかのよくあるデザインモデルについて話して、ブログを書いて、記録してください.
まず、デザインモードとは何でしょうか.設計モードは繰り返し使用され、多くの人が知っている分類符号化、コード設計経験の総括である.例えば、コードを戦争にたとえると、設計モデルは孫子兵法三十六計である.設計モードの目的は、コードを再利用し、コードを他人に理解されやすくし、コードの信頼性を保証することである.
単一のモードの役割は、アプリケーションコード全体のインスタンスが1つしかないことを保証することです.餓漢モードと怠け者モードの違いに分けられます.餓漢モードの特徴は、クラスをロードするのが遅いが、実行時にオブジェクトを取得する速度が速く、スレッドが安全であることです.怠け者モードの特徴は、クラスのロードが速いが、実行時にオブジェクトを取得する速度が遅く、スレッドが安全ではないことです.単例モードを作成するには:1、構造方法を私有化し、外部から直接オブジェクトを作成することを許さない2、クラスの唯一のインスタンスを宣言し、private staticで3を修飾し、public staticで修飾する方法を提供する
よく使われる単例パターンの書き方:餓漢式
public class Singleton {
    /**
     *      
     */
    private final static Singleton instance = new Singleton();
    private Singleton(){

    }
    public static Singleton getInstance(){
        return instance;
    }
}

これは餓漢式で最も簡単な書き方で、クラスをロードするときにインスタンス化が完了し、スレッドの安全を保証できますが、使用しないとメモリの無駄になります.
怠け者風
public class Singleton {
    /**
     *         
     */
    private static Singleton instance;
    private Singleton(){

    }
    public static Singleton getInstance(){
        if(instance ==null){
            instance = new Singleton();
        }
        return instance;
    }
}

これは怠け者式の簡単な書き方ですが、この書き方スレッドは安全ではありません.複数のスレッドが同時に呼び出されると、複数のインスタンスが現れる可能性があります.そのため、この方法は単一スレッドの単一例にのみ適しています.
以上のスレッドが安全でないという問題からsynchronizedキーワードを用いてメソッド上で同期を行うことができるが,クラスのインスタンスを取得するにはgetInstanceメソッドを実行して同期を行う必要があるが,このメソッドの目的は一度だけインスタンス化することであり,後でスレッドがインスタンスを取得したい場合は直接returnでよいため,この同期の方法はよくない.使用を推奨せず、二重検査を使用できる単一のパターンを具体的に改善します.以下のようにします.
public class Singleton {
    /**
     *                    
     */
    private static Singleton instance;
    private Singleton(){

    }
    public static Singleton getInstance(){
        if(instance ==null){
            synchronized(Singleton.class){
                if (instance ==null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

二重検査モードはスレッドの安全を保証し,インスタンス化のみを保証した.
アダプタモードアダプタモードは、1つのクラスのインタフェースを、お客様が望む別のインタフェースに変換し、インタフェースが互換性がないため一緒に動作できないクラスが一緒に動作できるようにします.コンビネーション方式は、コンビネーション方式を採用したアダプタを対象アダプタと呼び、被適合者を1つのオブジェクトとしてアダプタクラスにコンビネーションし、ターゲットインタフェースパッケージの被適合者を修正することを特徴とする.継承方式は、継承方式を採用したものをクラスアダプタと呼び、多重継承不互換インタフェースにより、ターゲットインタフェースのマッチングを実現し、単一のクラスに適合することを特徴とする.
アダプタモードの役割:1、透過性.アダプタを使用すると、クライアントは同じインタフェースを呼び出すことができるので、クライアントにとって透明です.このようにするのはもっと簡単で、もっと直接的で、もっとコンパクトです.2、再利用.既存のクラスを多重化し,既存のクラスと多重環境の要求が一致しない問題を解決した.3,低結合.ターゲットクラスとアダプタークラスをデカップリングし、アダプタクラスを導入することで、既存のコードを変更する必要がなく、既存のアダプタークラスを再利用します(開閉の原則に従います).
対象アダプタdemoは、携帯電話の充電に220 Vの電流を5 Vに変換する必要がある:包装されたクラス、すなわちsourceクラス:
public class AC220 {
    public int output220V(){
        int output = 220;
        return output;
    }
}

包装類(目標類):
/**
 *	      ,         
 */
public interface DC5 {
    int output5V();
}

アダプタクラス:
/**
 *             ,      ,      
 */
public class PowerAdapter implements DC5{
		private AC220 mAC220;
		public PowerAdapter(AC220 ac220){
        	this.mAC220 = ac220;
    }
    	@Override
    	public int output5V() {
        	int output = 0;
        	if (mAC220 != null) {
        		output = mAC220.output220V() / 44;
        	}
        	return output;
    }
}

主関数:
public static void main(String[] args) {
		PowerAdapter adapter = new PowerAdapter(new AC220());
	}

ポリシー・モード
ポリシー・モードは、可変部門をプログラムから抽象的にアルゴリズム・インタフェース(汎用部分)から分離し、このインタフェースの下で一連のアルゴリズム実装をそれぞれカプセル化し、互いに置き換えることができ、クライアント・プログラムがアルゴリズムから独立して変化する.
ポリシー・モードの実装:1、変化を分離することによって、ポリシー・オブジェクトに共通のインタフェースを定義します.2.上記の共通インタフェースを実現するポリシークラスを作成する.(複数の機能実装)3.ポリシーオブジェクトを使用するクラスにポリシーオブジェクトへの参照を保存します.4.ポリシーオブジェクトを使用するクラスで、ポリシーオブジェクトに対するsetterメソッドとgetterメソッドを実装するか、構築メソッドを使用して割り当てを完了します.
demo:インタフェースを定義し(ネットバンクの支払いのような抽象戦略であり、銀行の支払いを気にしないで、支払いの層を知っていればよい)、2つの整数を演算する方法を定義します.
public interface Strategy {
 	public abstract int calculate(int a,int b);
}

特定のアルゴリズムクラスを定義し、2つの整数の加算減算演算を実現します.
public class AddStrategy implements Strategy{
    @Override
    public int calculate(int a, int b) {
        return a+b;
    }
}

public class SubstractStrategy implements Strategy{
    @Override
    public int calculate(int a, int b) {
        return a-b;
    }
}

public class MultiplyStrategy implements Strategy {
    @Override
    public int calculate(int a, int b) {
        return a*b;
    }
}

public class DivisionStrategy implements Strategy{
    @Override
    public int calculate(int a, int b) {
        if(b!=0){
         	return a/b;
        }
        else {
            throw new RuntimeException("      ");
        }
    }
}

特定の環境ロールを定義し、Strategyインタフェースの参照を持ち、getterメソッドとsetterメソッドがポリシーの交換を完了し、環境ロールでインタフェースを呼び出すメソッドが動作を完了します.
public class Context {
    
    private Strategy strategy;
    
    public Context(Strategy strategy) {
        super();
        this.strategy = strategy;
    }

    public Strategy getStrategy() {
        return strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    
    public int calculate(int a,int b){
        return strategy.calculate(a, b);
    }
}

クライアントは呼び出し時に、環境ロールに対応するアルゴリズムクラスを設定するだけで、対応する結果を得ることができます.
public class StrategyTest {
    public static void main(String[] args) {
        //  
        Context add=new Context(new AddStrategy());
        System.out.println(add.calculate(10, 5));
        //  
        Context sub=new Context(new SubstractStrategy());
        System.out.println(sub.calculate(3, 2));
        //  
        Context mul=new Context(new MultiplyStrategy());
        System.out.println(mul.calculate(6, 8));
        //  
        Context div=new Context(new DivisionStrategy());
        System.out.println(div.calculate(90, 9));    
    }
}

ポリシー・モードの長所と短所:利点:1、組み合わせを使用して、アーキテクチャをより柔軟にします.2、弾力性に富み、変化によく対応できます(開閉原則).3、コードの多重性がよくなります(継承に対して).4、大量の条件文が解消されました.(多くの場合、通常switch case、if elseであるが、ポリシーモードがこれを最適化している).欠点:1、顧客コードは、各ポリシー実装の詳細を理解する必要があり、各実装クラスの具体的な実装を知る必要がある.2、多くのオブジェクトの数を増やす.
ポリシーモードの適用シーン:1、多くの関連クラスは動作の違いにすぎません.2、実行時に異なるアルゴリズムバリエーションを選択します.3条件文で複数のブランチから1つを選択します.
プロキシ・モードは、このオブジェクトへのアクセスを制御するために他のオブジェクトにプロキシを提供することになります.エージェント・オブジェクトは、機能サービスを削除したり、追加のサービスを追加したりする仲介として機能します.ソースコードを変更せずに削除機能を追加します.
静的エージェント:エージェントと被エージェントオブジェクトは、エージェントの前に決定され、同じインタフェースを実装するか、同じ抽象クラス、親クラスを継承します.また、複数のエージェントを連続的にエージェントすることができます.
demo:インタフェースMoveable
public interface Moveable {
    void run();
}

ターゲットオブジェクトrun:
public class Run implements Moveable{
    @Override
    public void run() {
        System.out.println("run run run");
    }
}

エージェントcar:
public class Car implements Moveable{

    private Moveable moveable;

    public Car(Moveable moveable){
        this.moveable=moveable;
    }

    @Override
    public void run() {
        System.out.println("car run");
    }
}

テストクラス
public class Test {
    public static void main(String[] args) {
        Run run = new Run();
        Car car = new Car(run);
        car.run();
    }
}

静的エージェントの概要:利点:ターゲットオブジェクトの機能を変更することなく、ターゲット機能を拡張できます.欠点:エージェントオブジェクトはターゲットオブジェクトと同じインタフェースを実装する必要があるため、多くのエージェントクラスが現れ、元のインタフェースがメソッドを追加すると、ターゲットオブジェクトとエージェントオブジェクトが維持されます.
ダイナミックエージェント
動的エージェント実装手順:1 invokeメソッドを実装する必要がある実装インタフェースInvocationHandlerのクラスを作成します.2,エージェントされるクラスおよびインタフェースを作成する.3,Proxyの静的メソッドを呼び出し,プロキシクラスnewProxyInstance(ClassLoader loader,Class[]interfaces,InvocationHandler h)4を作成し,プロキシ呼び出しメソッドを介して
demo:元のインタフェース:
public interface Moveable {
    void run();
}

代理オブジェクト:
public class Run implements Moveable{
    @Override
    public void run() {
        System.out.println("run run run");
    }
}

動的エージェントクラス:
public class TimeHandler implements InvocationHandler {

    private Object object;

    public TimeHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("run starting");
        method.invoke(object);
        System.out.println("run ending");
        return null;
    }
}

mainメソッドテスト:
public class Test {
    public static void main(String[] args) {
        /**
         *       
         */
        //    
        Run run = new Run();
        InvocationHandler h = new TimeHandler(run);
        Class<?> cls = run.getClass();

        /**
         * loader     
         * interfaces     
         * h invocationHandler
         */
        Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
                cls.getInterfaces(),h);
        m.run();
    }
}

cglibエージェントモード
動的エージェントとcglibエージェントの違い:1,jdk動的エージェントは,エージェントがインタフェースを実現したクラスのみを実現し,インタフェースを実現しないクラスはjdk動的エージェントを実現できない.2,cglibダイナミックエージェントはクラスに対してエージェントを実現し,指定したターゲットクラスに対してサブクラスを生成し,メソッドブロック技術によりすべての親メソッドの呼び出しをブロックする.3,cglibダイナミックエージェントのクラスはstaticとfinalではありません.それは継承できないからです.
demo:
まずcglibのjarパッケージをインポート
ターゲットオブジェクト:
public class UserDao {

    public void save() {
        System.out.println("----      !----");
    }
}

Cglibエージェントクラス:
/**
 * Cglib      
 *  UserDao              
 */
public class ProxyFactory implements MethodInterceptor{
    //      
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }
    //             
    public Object getProxyInstance(){
        //1.   
        Enhancer en = new Enhancer();
        //2.    
        en.setSuperclass(target.getClass());
        //3.      
        en.setCallback(this);
        //4.    (    )
        return en.create();
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("  ");
        //         
        Object returnValue = method.invoke(target, args);
        System.out.println("  ");
        return returnValue;
    }
}

主関数のテスト:
/**
 *    
 */
public class Test {
    public static void main(String[] args) {
        //    
        UserDao target = new UserDao();

        //    
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();

        //         
        proxy.save();
    }
}

工場モード
ファクトリモード概念:オブジェクトをインスタンス化し、new操作の代わりにファクトリメソッドを使用します.工場モデルには工場方法モデルと抽象工場モデルが含まれ、抽象工場モデルは工場方法モデルの開拓である.
ファクトリモードの意図:オブジェクトを作成するためにインタフェースを定義しますが、サブクラスにどのクラスがインスタンス化される必要があるかを決定させ、ファクトリメソッドはインスタンス化の作業をサブクラスに延期して実現します.
ファクトリモード適用シーン:1、類似のオブジェクトのセットを作成する必要があります.2,符号化時にどの種類のインスタンスを作成する必要があるか予見できない.3、システムは拡張性を考慮する必要があり、製品クラスインスタンスがどのように作成され、組み合わせられ、表現されるかの詳細に依存してはならない.
ファクトリモードはjava springフレームワーク依存注入と結合することが多く,具体的にはcontroller層,dao層,service層および具体的にimplを実装し,階層呼び出しとして表現されるが,互いに干渉せず,新しい機能を追加する必要がある場合は,新しい方法を追加するだけでよいが,ここでは例を挙げない.
以上、よくあるデザインパターンをいくつか書きましたが、問題があれば、コメントを残して直してください.