C# 이것저것/C# 디자인패턴

[C#] Bridge 패턴

agingcurve 2024. 4. 6. 16:10
반응형

카테고리 : 구조 패턴

개요 : Brdige 패턴은 실제 로직을 담은 구현 클래스를 직접 호출하는게 아닌, 이 구현체로 부터 추상층 클래스를 분리하여 클라이언트는 이 추상층 클래스를 거쳐 구현체 클래스를 사용하는 방식이다.

 

Bridge 패턴은 클래스 자체와 그 클래스가 동작하는 구체적인 구현 내용을 분리하는 것으로, 클래스 자체는 추상화 하여 추상층 클래스로 만들고, 클래스가 실제 무슨 일을 하는 지는 별도의 구현체 클래스로 만들게 된다.

 

일반적인 객체지향 프로그래밍(OOP)에서의 서브클래스들은 상위의 부모/추상 클래스를 상속하여 서브클래스에서 다른 행위들을 정의하게 된다. 이러한 객체지향 프로그래밍의 서브클래스 상속은 컴파일 시, 부모/ 추상 클래스(abstraction)가 구현체인 서브클래스(implemnetation)과 긴밀하게 바인딩 되어, 런타임 시 이를 변경하기 쉽지 않다.

ㅂ반면, Bridge 패턴은 추상층과 구현체를 둘로 분리해, 교량이 설치되어 있는 것처럼 구현한 것으로, 추상층 클래스를 사용할 때 특정한 구현체 클래스를 선택하여 사용할 수 있다.

 

 

 

namespace Bridge_pattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Client.HowToUse();
        }

        public abstract class Abstraction
        {
            public IImplementor IImplementor { get; set; }
            public abstract void Operation();
        }
        
        // Abstraction 클래스
        public class RefindAbstraction : Abstraction
        {
            public override void Operation()
            {
                IImplementor.ImplemntorOperation();
            }
        }

        // Implemnetor 인터페이스
        public interface IImplementor
        {
            void ImplemntorOperation();
        }

        // Implemnetor 클래스들
        public class ImplemnetorA : IImplementor
        {
            public void ImplemntorOperation()
            {
                Console.WriteLine("ImplemnetorA : ImplemntorOperation()");
            }
        }

        public class ImplemntorB : IImplementor
        {
            public void ImplemntorOperation()
            {
                Console.WriteLine("ImplemnetorB : ImplemntorOperation()");
            }
        }

        // 클라이언트
        class Client
        {
            public static void HowToUse()
            {
                Abstraction ab = new RefindAbstraction();

                // ImplemntorA 구현체 사용
                ab.IImplementor = new ImplemnetorA();
                ab.Operation();

                // ImplemntorB 구현체를 사용
                ab.IImplementor = new ImplemntorB();
                ab.Operation();
            }
        }
    }
}

 

 

Bridge 패턴은 크게 추상층부분과 구현체 부분으로 나누어 지며, 추상층에 해당하는 Abstraction 인터페이스와 이를 구현한 RefindAbstraction 클래스, 그리고 구현체 부분에 해당하는 Implemntor 인터페이스와 이를 구현한 ConcreteImplemnetor 클래스 등으로 구성된다.

Abstraction 인터페이스는 클라이언트가 사용하는 인터페이스로서 Implemntor를 참조하는 멤버를 가지게 된다. Implementor 인터페이스는 ConcreteImplemntor 객체들에 대한 인터페이스로서 이는 Abstraction 인터페이스와 분리되어 동일하지 않아도 된다.

 

아래 예시는 모바일 장비에 현재 WiFi와 4G 네트워크가 지원되는데, 향후 여러 다른 네트워크가 추가로 지원될 예정이라고 가정해볼 때, 구현체(Implemnetation) 부분에서 Implemntor 인터페이스가 지원되는 한 계속 다른 네트워크 구현체들을 독립적으로 추가할 수 있다.

 

using System.Net.Http.Headers;
using System.Text;

namespace INetworkBridge
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Client cls = new Client();
            cls.HowToUse();
        }
        // Implementor 인터페이스
        public interface INetworkImplemnetor
        {
            bool Connect();
            void Send(byte[] data);
            byte[] Receive();
            bool Disconnect();
        }

        // Concrete Implementor 클래스들
        public class WiFiImplemntor : INetworkImplemnetor
        {
            private byte[] data;
            public bool Connect()
            {
                Console.WriteLine("WiFi: Connect");
                return true;
            }
            public bool Disconnect()
            {
                Console.WriteLine("WiFi: Disconnect");
                return true;
            }

            public void Send(byte[] data) => this.data = data;

            public byte[] Receive() => this.data;
        }

        public class _4GImplemntor : INetworkImplemnetor
        {
            private byte[] data;

            public bool Connect() 
            {
                Console.WriteLine("4G: Connect");
                return true;
            }
            public bool Disconnect()
            {
                Console.WriteLine("4G: Disconnect");
                return true;
            }

            public void Send(byte[] data) => this.data = data;
            public byte[] Receive() => this.data;
        }

        // 미리 구현할 네트워크 구현체
        // public class _5GImplemntor : INetworkImplemnetor{}        
        // public class _6GImplemntor : INetworkImplemnetor{}


        /* 추상층 */
        public interface INetworkAbstraction
        {
            INetworkImplemnetor Network { get; set; }
            void SendReceive(byte[] data);
        }

        // RefindAbstraction 클래스
        public class NetworkAbstraction : INetworkAbstraction
        {
            public INetworkImplemnetor Network { get; set;}
            public void SendReceive(byte[] bytes)
            {
                Network.Connect();
                Network.Send(bytes);
                bytes = Network.Receive();
                Network.Disconnect();  
            }
        }

        //클라이언트
        class Client
        {
            public void HowToUse()
            {
                INetworkAbstraction net = new NetworkAbstraction();
                net.Network = GetNetwork();
                net.SendReceive(Encoding.UTF8.GetBytes("DATA"));
            }

            private INetworkImplemnetor GetNetwork()
            {
                Random rnd = new Random();

                if(rnd.Next() % 2 == 0)
                {
                    return new WiFiImplemntor();
                }
                else
                {
                    return new _4GImplemntor(); 
                }

            }
        }
    }
}

 

추상층(Abstraction) 부분은 INetworkAbstraction 인터페이스와 이를 구현한 NetworkAbstraction 클래스에서는 간단히 SendReceive 메서드를 사용하여 네트워크를 연결하고 메세지를 보내고 받는 부분을 구현하고 있다. 클라이언트가 INetworkAbstraction를 사용하기 위해서는 먼저 INetworkImplementor 타입인 Network 속성을 설정한 후, 즉 어떤 네트워크를 사용할 지 미리 설정한 후, SendReceive 메서드를 사용하면 된다.

 

Bridge 패턴 사례

Bridge 패턴의 사례로서 ODBC, ADO.NET 등과 같은 데이타베이스 드라이버 아키텍처를 들 수 있다. 예를 들어, ODBC는 공통된 ODBC API 인터페이스 계층(추상층)과 각 데이타베이스마다 다르게 구현된 ODBC Driver 계층(구현층)으로 나뉘어 진다. 구현체(Implementation)에 해당하는 ODBC Driver들은 DB마다 다르게 구현되지만, 클라이언트는 공통된 ODBC API 인터페이스를 사용하여 데이타베이스를 엑세스하게 된다.

 

'C# 이것저것 > C# 디자인패턴' 카테고리의 다른 글

[C#] Proxy 패턴  (0) 2024.04.15
[C#] Decorator 패턴  (0) 2024.04.15
[C#] Adapter 패턴  (0) 2024.03.31
[C#] Object Pool 패턴  (0) 2024.03.31