Jaebi의 Binary는 호남선

Structural Patterns - Adapter 본문

공부

Structural Patterns - Adapter

jaebijae 2024. 6. 1. 19:46

목차

    Adapter (wrapper)

    • 클래스의 인터페이스를 사용자가 기대하는 인터페이스 형태로 변환, 서로 일치 하지 않는 인터페이스를 갖는 클래스를 함께 동작 시킴
    • 인터페이스는 호환되지 않을 수 있지만 내부 기능은 필요에 적합해야 함
    • 동기: 3rd party 라이브러리를 사용하는데 특정 인터페이스를 확용해야 하는 클래스가 있지만 호환이 안됨 → 코드와 라이브러리의 코드 사이에 wrapping하는 Adapter 클래스를 만들어 사용
    • 사용:
      • 기존 클래스를 사용하고 싶은데 인터페이스가 맞지 않을 때
      • 이미 만든것을 재사용 하고자 하나 이 재사용 가능한 라이브러리를 수정할 수 없을때

    • 구조:
      • 클래스 adapter는 다중 상속을 활용하여 한 인터페이스를 다른 인터페이스에 적용
      • 객체 adapter는 객체 합성을 써서 적용
      • Target: 사용자가 사용할 분야에 종속적인 인터페이스를 정의
      • Client: Target 인터페이스를 만족하는 객체와 동작할 대상
      • Adaptee: 인터페이스의 적응이 필요한 기존 인터페이스를 정의, 적응 대상자
      • Adapter: Target 인터페이스에 Adaptee의 인터페이스를 적응시키는 클래스
    • 장점:
      • Class Adapter
        • Adapter 클래스는 Adaptee 클래스를 상속함으로 Adaptee에 정의된 행동을 재정의 할 수 있음
        • 한개의 Adapter만 사용하여 Adaptee로 가기 위한 추가적인 포인터 간접화가 필요 없음
      • Object Adapter
        • Adapter 클래스는 하나만 존재해도 수많은 Adaptee 클래스들과 동작 가능
    • Sample Code:
    import 'package:flutter/material.dart';
    import 'dart:convert';
    void main() {
      runApp(MyApp());
    }
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            body: Center(
              child: UserWidget(adapter: JsonUsersAdapter()),
            ),
          ),
        );
      }
    }
    class UserWidget extends StatefulWidget {
      final UsersAdapterInterface adapter;
      const UserWidget({
        required this.adapter,
      });
      @override
      UserWidgetState createState() => UserWidgetState();
    }
    class UserWidgetState extends State<UserWidget> {
      final List<User> users = [];
      void _getContacts() {
        setState(() {
          users.addAll(widget.adapter.getUsers());
        });
      }
      @override
      void initState() {
        super.initState();
        _getContacts();
      }
      @override
      Widget build(BuildContext context) {
        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            for (var user in users) Text(user.name ?? '')
          ],
        );
      }
    }
    class User {
      String? name;
      String? email;
      int? age;
      User({
        required this.name,
        required this.email,
        required this.age,
      });
      User.fromJson(Map<String, dynamic> json) {
        name = json['name'];
        email = json['email'];
        age = json['age'];
      }
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = Map<String, dynamic>();
        data['name'] = name;
        data['email'] = email;
        data['age'] = age;
        return data;
      }
    }
    // fake api to return info as json string
    class JsonUsersAPI {
      final String _usersJson = '''
      {
        "users": [
          {
            "name": "John Doe (JSON)",
            "email": "johndoe@json.com",
            "age": 24
          }
        ]
      }
      ''';
      String getUsersJson() {
        return _usersJson;
      }
    }
    // fake api to return info as xml string
    class XmlUsersAPI {
      final String _usersXml = '''
      <?xml version="1.0"?>
      <users>
        <user>
          <name>John Doe (XML)</name>
          <email>johndoe@xml.com</email>
          <age>24</age>
        </user>
      </users>
      ''';
      String getUsersXml() {
        return _usersXml;
      }
    }
    // an interface that unifies adapters and requires them to implement method
    abstract class UsersAdapterInterface {
      List<User> getUsers();
    }
    // adapter that implements method in interface
    class JsonUsersAdapter implements UsersAdapterInterface {
      final JsonUsersAPI _api = JsonUsersAPI();
      @override
      List<User> getUsers() {
        // parse json string to required return type
        final usersJson = _api.getUsersJson();
        final usersMap = jsonDecode(usersJson) as Map<String, dynamic>;
        final usersMapList = usersMap['users'] as List;
        final userList = usersMapList.map((json) {
          final user = User.fromJson(json);
          return user;
        }).toList();
        return userList;
      }
    }
    class XmlUsersAdapter implements UsersAdapterInterface {
      final XmlUsersAPI _api = XmlUsersAPI();
      @override
      List<User> getUsers() {
        // parse xml string to required return type
        return [];
      }
    }
    • Target: `UsersAdapterInterface`
    • Client: `UserWidget`
    • Adaptees: `JsonUsersAPI`, `XmlUsersAPI`
    • Adapters: `JsonUserAdapter`, `XmlUsersAdapter`

    '공부' 카테고리의 다른 글

    Behavioral Patterns - Template  (0) 2024.06.01
    Behavioral Patterns - Adapter  (0) 2024.06.01
    Creational Patterns - Factory Method  (0) 2024.06.01
    Creational Patterns - Singleton  (2) 2024.06.01
    Creational Patterns - Builder  (0) 2024.06.01