構造設計モード(1)-アダプタモード、ブリッジモード、複合モード


アダプタモード


コンセントのことをよく話します.110 vを220 vに挿入するか、逆に互換性のあるアダプタを使用します.
既存のコードをクライアントが使用するインタフェースの実装体のモードに変換する
->クライアントの使用インタフェースに従わない既存のコードを再利用できます.
public interface UserDetails {

    String getUsername();
    String getPassword();
}

public interface UserDetailsService {

    UserDetails loadUser(String username);

}
public class LoginHandler {

    UserDetailsService userDetailsService;

    public LoginHandler(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public String login(String username, String password) {
        UserDetails userDetails = userDetailsService.loadUser(username);
        if (userDetails.getPassword().equals(password)) {
            return userDetails.getUsername();
        } else {
            throw new IllegalArgumentException();
        }
    }
}
クライアントコード
簡単なログイン処理ロジック.他のアプリケーションも再利用できます.
ターゲットコード
public class Account {

    private String name;
    private String password;
    private String email;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

}


public class AccountService {

    public Account findAccountByUsername(String username) {
        Account account = new Account();
        account.setName(username);
        account.setPassword(username);
        account.setEmail(username);
        return account;
    }

    public void createNewAccount(Account account) {

    }

    public void updateAccount(Account account) {

    }

}
Adapteeに対応するコード、電子メールを受信しない、電話番号を受信するなど、異なる構成が可能です.(アプリケーションによって異なる場合があります.)
public class AccountUserDetails implements UserDetails {

    private Account account;

    public AccountUserDetails(Account account) {
        this.account = account;
    }

    @Override
    public String getUsername() {
        return account.getName();
    }

    @Override
    public String getPassword() {
        return account.getPassword();
    }
}

public class AccountUserDetailsService implements UserDetailsService {   //맞춰줘야되는 타겟 인터페이스 구현
    private AccountService accountService; // 어댑티에 해당되는 코드사용

    public AccountUserDetailsService(AccountService accountService) {
        this.accountService = accountService;
    }

    @Override
    public UserDetails loadUser(String username) {
        return new AccountUserDetails(accountService.findAccountByUsername(username));
    }
}
アダプタが作成されます.
 public static void main(String[] args) {
        AccountService accountService = new AccountService();
        UserDetailsService userDetailsService = new AccountUserDetailsService(accountService);
        LoginHandler loginHandler = new LoginHandler(userDetailsService);
        String login = loginHandler.login("youngjin", "940505");
        System.out.println(login);
    }

既存のコードに触れず、アダプタのみが追加されました.
弱いターゲットや適応性だけでコードを変更することはできません.外部ライブラリは非常に役に立つかもしれません.
コードを直接修正できると言ったら
Account Implements UserDetails(ターゲットインタフェースを直接実装)
長所
  • 既存のコードを変更することなく、必要なインタフェースインプリメンテーションを作成して再使用
    =>OCPに近い特性
  • の既存のコードが行う作業と、特定のインタフェースインプリメンテーションに変換される作業を異なるクラスに分けて管理することができる.
    =>SRPに近い特性
  • 短所
  • の新しいレベルがあり、複雑度が増加する可能性があります.
    =>インタフェースを実装するために既存のコードを変更することは、
  • の選択肢である可能性がある.
    SRP(単一責任原則)の立場から見ると、区分等級は対象原則に近い

    使用例


    Java
     public static void main(String[] args) {
            // collections
            List<String> strings = Arrays.asList("a", "b", "c"); // 배열을 리스트로 변환
            Enumeration<String> enumeration = Collections.enumeration(strings); // Enumeration = 어댑터 strings = 어댑티 enumberration=타겟겟        ArrayList<String> list = Collections.list(enumeration);
    
            // io
            try(InputStream is = new FileInputStream("input.txt"); 
            // String을줫지만 타겟인터페이스인 InputStream에 맞춰서 나옴
                InputStreamReader isr = new InputStreamReader(is); 
                // InputStream을줫지만 InputStreamReader 를 줬다.
                BufferedReader reader = new BufferedReader(isr)) {
                while(reader.ready()) {
                    System.out.println(reader.readLine());
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    SpringではHandlerアダプタでも使えます.

    ブリッジパターン



    抽象=高度な抽象論理
    これらのベンチクラスが継承されると、抽象概念はより多くの変形と異なる構造に拡張されます.
    実装=ステータス、アクション、プラットフォーム固有コード
    public interface Champion extends Skin {
    
        void move();
        void skillQ();
        void skillW();
        void skillE();
        void skillR();
    
    }
    
    public class KDA아리 implements Champion {
    
        @Override
        public void move() {
            System.out.println("KDA 아리 move");
        }
    
        @Override
        public void skillQ() {
            System.out.println("KDA 아리 Q");
        }
    
        @Override
        public void skillW() {
            System.out.println("KDA 아리 W");
        }
    
        @Override
        public void skillE() {
            System.out.println("KDA 아리 E");
        }
    
        @Override
        public void skillR() {
            System.out.println("KDA 아리 R");
        }
    
        @Override
        public String getName() {
            return null;
        }
    }
    
    もし私が新しいチャンピオンを追加したら?あるいは
    新しい肌を追加したら?
    public class 정복자아리 implements Champion {
        @Override
        public void move() {
            System.out.println("정복자 아리 move");
        }
    
        @Override
        public void skillQ() {
            System.out.println("정복자 아리 Q");
        }
    
        @Override
        public void skillW() {
            System.out.println("정복자 아리 W");
    
        }
    
        @Override
        public void skillE() {
            System.out.println("정복자 아리 E");
        }
    
        @Override
        public void skillR() {
            System.out.println("정복자 아리 R");
        }
    
        @Override
        public String getName() {
            return null;
        }
    }
     public static void main(String[] args) {
            Champion kda아리 = new KDA아리();
            kda아리.skillQ();
            kda아리.skillR();
        }
    新しい征服者アリの皮膚があれば階層が大きくなり、Childクラスを作成する過程には重複コードが多く、他のクラスと差が少ないような気がします.
    
    public interface Skin {
        String getName();
    }
    
    public class DefaultChampion implements Champion {
    
        private Skin skin; // 스킨인터페이스 사용
    
        private String name;
    
        public DefaultChampion(Skin skin, String name) {
            this.skin = skin;
            this.name = name;
        }
    
        @Override
        public void move() {
            System.out.printf("%s %s move\n", skin.getName(), this.name);
        }
    
        @Override
        public void skillQ() {
            System.out.printf("%s %s Q\n", skin.getName(), this.name);
        }
    
        @Override
        public void skillW() {
            System.out.printf("%s %s W\n", skin.getName(), this.name);
        }
    
        @Override
        public void skillE() {
            System.out.printf("%s %s E\n", skin.getName(), this.name);
        }
    
        @Override
        public void skillR() {
            System.out.printf("%s %s R\n", skin.getName(), this.name);
        }
    
        @Override
        public String getName() {
            return null;
        }
    }
    
    チャンピオンを増やしたい?
    public class 아리 extends DefaultChampion {
    
        public 아리(Skin skin) {
            super(skin, "아리");
        }
    }
    
    public class 아칼리 extends DefaultChampion{
    
        public 아칼리(Skin skin) {
            super(skin, "아칼리");
        }
    }
    
    肌作りたい?
    public class KDA implements Skin{
        @Override
        public String getName() {
            return "KDA";
        }
    }
    
    public class PoolParty implements Skin {
        @Override
        public String getName() {
            return "PoolParty";
        }
    }
    
    
    これがモデルの核心です.現在の階層のみを拡張し、他の階層には触れません.
     public static void main(String[] args) {
            Champion kda아리 = new 아리(new KDA());
            kda아리.skillQ();
            kda아리.skillW();
    
            Champion poolParty아리 = new 아리(new PoolParty());
            poolParty아리.skillR();
            poolParty아리.skillW();
        }
    クライアントコードにもインタフェースが使用されています.
    (抽象部分)

    ブリッジパターン
    長所
  • 抽象コード特定のコード変更なしで独立して拡張
    =>OCP原則達成
  • 抽象コードは、特定のコードから分離することができる.
  • 既存のコードの再使用、重複コードの削減など、さまざまな追加の利点があります.
    短所
  • 階層が増加し、複雑さが増加する可能性があります.
  • ブリッジモードの使用例


    JDBC
          Class.forName ("org.h2.Driver");
    
     try (Connection conn = DriverManager.getConnection ("jdbc:h2:mem:~/test", "sa","")) {
    
                String sql =  "CREATE TABLE  ACCOUNT " +
                        "(id INTEGER not NULL, " +
                        " email VARCHAR(255), " +
                        " password VARCHAR(255), " +
                        " PRIMARY KEY ( id ))";
    
                Statement statement = conn.createStatement();
                statement.execute(sql);
    
    //            PreparedStatement statement1 = conn.prepareStatement(sql);
    //            ResultSet resultSet = statement.executeQuery(sql);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
    jdbcコードの一部
    これはResultSet、Statement、Connectionブリッジモードで抽象化することに相当します.
    具体的なコードはDriver部分です.
    java.sql.driverのいずれかのorg.h2.Driverを持ってきました.

    複合モード


    グループ全体と単一のグループを同時に処理できる540 ner
  • クライアントの立場から見ると、これは全体または部分的に同じ要素として認識できる階層である.
  •     public static void main(String[] args) {
            Item doranBlade = new Item("도란검", 450);
            Item healPotion = new Item("체력 물약", 50);
    
            Bag bag = new Bag();
            bag.add(doranBlade);
            bag.add(healPotion);
    
            Client client = new Client();
            client.printPrice(doranBlade); 
            client.printPrice(bag);
        }
    
        private void printPrice(Item item) {
            System.out.println(item.getPrice());
        }
    
        private void printPrice(Bag bag) {
            int sum = bag.getItems().stream().mapToInt(Item::getPrice).sum();
            System.out.println(sum);
        }
    クライアントの場合、
    かばんの中の品物の値段を合わせて印刷したいです.
    クライアントが多すぎることを知っておく必要があります
    クライアントコードのポイント、
    かばんの中にかばんが入っています.ちょっと待ってください.

    コンポーネントと呼ばれるインタフェースを定義します.
    public interface Component {
        int getPrice();
    }
    重要なのは、汎用操作をインタフェースに配置する必要があることです.
    public class Item implements Component {
    
        private String name;
        private int price;
    
        public Item(String name, int price) {
            this.name = name;
            this.price = price;
        }
    
        @Override
        public int getPrice() {
            return this.price;
        }
    }
    インタフェースサポートの再構成方法getPrice()
    public class Bag implements Component {
    
        private List<Component> components = new ArrayList<>();
    
        public void add(Component component) {
            components.add(component);
        }
    
        public List<Component> getComponents() {
            return components;
        }
    
        @Override
        public int getPrice() {
            return components.stream().mapToInt(Component::getPrice).sum();
        }
    }
    BagはLeafを参照せずにコンポーネントを参照する必要があります.
    クライアントが多すぎることを知る必要はありません.引合の責任者がBagに来ました.
    public class Client {
    
        public static void main(String[] args) {
            Item doranBlade = new Item("도란검", 450);
            Item healPotion = new Item("체력 물약", 50);
    
            Bag bag = new Bag();
            bag.add(doranBlade);
            bag.add(healPotion);
    
            Client client = new Client();
            client.printPrice(doranBlade);
            client.printPrice(bag);
        }
    
        private void printPrice(Component component) { 
        // 내가사용하는 타입의 변수들은 가능한한 추상적 인터페이스를 쓰는것이 좋은영향을 준다. 
            System.out.println(component.getPrice());
        }
    
    
    }
    

    私が使用しているタイプの変数は、できるだけ抽象的なインタフェースを使用することが望ましい.(printPriceのコンポーネントパラメータなど)
    クライアントが価格の具体的な情報をどのように取得するか分からないのも関係ありません.

    複合モードの利点

  • は、複雑なツリー構造を容易に使用することができる.
  • 多形性および再帰を利用することができる.
    =>クライアントの場合、新しいタイプのLeafまたはComponentが追加されても、クライアントコードは変更されません.
    OCP原則
  • クライアントコードを変更せずに、新しい別名タイプを追加できます.
  • 短所

  • は、あまりにも一般化されている可能性があります.(汎用インタフェースを定義する必要があるため)
    =>もし副作用があったら、私は今デザインモードに依存していますか?他の方法はもっといいですか?
  • 複合パターンの使用例

      public static void main(String[] args) {
            JFrame frame = new JFrame();
    
            JTextField textField = new JTextField();
            textField.setBounds(200, 200, 200, 40);
            frame.add(textField);
    
            JButton button = new JButton("click");
            button.setBounds(200, 100, 60, 40);
            button.addActionListener(e -> textField.setText("Hello Swing"));
            frame.add(button);
    
            frame.setSize(600, 400);
            frame.setLayout(null);
            frame.setVisible(true);
        }
    これはジャワのスイングです.
    フレームスペースにボタンやテキストスペースなどを追加できます.
    JFrameはCompositeに相当し、Jbutton、JTextFeildはLeafの構造である.