20-0413 가교 패턴(BridgePattern)

최용국·2020년 4월 12일
0

디자인패턴

목록 보기
6/6

가교 패턴(BridgePattern)

우리가 코드를 짜다보면 의도치 않게 많은 파생클래스를 만들어 낼 때가 있다. 정말 필요한 파생클래스 일 수도 있지만 그 모델의 디자인을 다시한번 살펴 볼 필요가 있다. 파생된 클래스에서 정의되고 있는게 다른 파생클래스에서도 정의하고있는 반복적인건 아닌지를 말이다. 이때 필요한게 가교 패턴이다. 추상을 해야하는 부분과 구현 부분을 나누는 것이다.

다음 예제는 Person 클래스가 있고 Person클래스를 파일로 직렬화와 역직렬화를 도와주는 PersonManager 클래스가 있다. PersonManager 클래스는 ConvertManager클래스를 상속받으며 추상화 되어있는 Read, Write를 정의해준다. PersonManager 클래스에서는 Read, Write할 때 Person객체가 갖고있는 주민번호인 PersonalId 프로퍼티를 DES 암호화해준다. 직렬화, 역직렬화 할 수 있는 파일 형태는 Yaml, Json 두 가지를 지원한다. ConvertManager클래스 안에는 FileConverter 추상 클래스를 배열로 갖고 있다. FileConverter 클래스는 Deserialize, Serialize 함수를 추상화한다. MyJsonConverter, MyYamlConverter클래스는 FileConverter 클래스를 상속받아 함수를 정의해 준다.

interface IConverter

public interface IConverter<T>
{
    string FileName { get; set; }
    void Serialize(T item);
    T Deserialize();
}

class FileConverter

public abstract class FileConverter<T> : IConverter<T> where T : class
{
    protected FileConverter(string fileName)
    {
        FileName = fileName;
    }
    public string FileName { get; set; }
    public abstract T Deserialize();
    public abstract void Serialize(T item);
}

class MyYamlConverter

public class MyYamlConverter<T> : FileConverter<T> where T : class
{
    public MyYamlConverter(string fileName) : base(fileName)
    {
    }

    public override T Deserialize()
    {
        var builder = new DeserializerBuilder().Build();
        using (StreamReader reader = new StreamReader(File.Open(this.FileName, FileMode.Open, FileAccess.Read)))
        {
            return builder.Deserialize(reader, typeof(T)) as T;
        }
    }

    public override void Serialize(T item)
    {
        var builder = new SerializerBuilder().Build();
        using (StreamWriter writer = new StreamWriter(File.Create(this.FileName)))
        {
            builder.Serialize(writer, item);
        }
    }
}

class MyJsonConverter

public class MyJsonConverter<T> : FileConverter<T> where T : class
{
    public MyJsonConverter(string fileName) : base(fileName)
    {
    }


    public override T Deserialize()
    {
        using (StreamReader reader = new StreamReader(File.Open(this.FileName, FileMode.Open, FileAccess.Read)))
        {
            return JsonConvert.DeserializeObject<T>(reader.ReadToEnd());
        }
    }

    public override void Serialize(T item)
    {
        using (StreamWriter writer = new StreamWriter(File.Create(this.FileName)))
        {
            writer.WriteLine(JsonConvert.SerializeObject(item));
        }
    }
}

enum ConvertEnum

public enum ConvertEnum
{
    Yaml = 0,
    Json
}

class ConvertManager

public abstract class ConvertManager<T> where T : class
{
    protected ConvertManager(string fileName)
    {
        FileName = fileName;
        FileConverters = new FileConverter<T>[2] {
            new MyYamlConverter<T>(FileName),
            new MyJsonConverter<T>(FileName)
        };
    }

    public string FileName { get; private set; }
    public FileConverter<T>[] FileConverters { get; private set; }

    public abstract ConvertEnum ConvertEnum { get; set; }
    public abstract void Write(T item);
    public abstract T Read();
}

class EZProtector

public class EZProtector
{
    private byte[] Key { get; set; }


    public EZProtector(string key)
    {
        this.Key = ASCIIEncoding.ASCII.GetBytes(key);
    }

    public string Encrypt(string data)
    {
        DESCryptoServiceProvider provider = GetProvider();

        MemoryStream stream = new MemoryStream();
        var cryptoStream = new CryptoStream(stream, provider.CreateEncryptor(), CryptoStreamMode.Write);

        byte[] dataArray = Encoding.UTF8.GetBytes(data);

        cryptoStream.Write(dataArray, 0, dataArray.Length);
        cryptoStream.FlushFinalBlock();

        return Convert.ToBase64String(stream.ToArray());
    }
    public string Decrypt(string data)
    {
        DESCryptoServiceProvider provider = GetProvider();

        MemoryStream stream = new MemoryStream();
        var cryptoStream = new CryptoStream(stream, provider.CreateDecryptor(), CryptoStreamMode.Write);

        byte[] dataArray = Convert.FromBase64String(data);

        cryptoStream.Write(dataArray, 0, dataArray.Length);
        cryptoStream.FlushFinalBlock();

        return Encoding.UTF8.GetString(stream.GetBuffer());
    }

    private DESCryptoServiceProvider GetProvider() => new DESCryptoServiceProvider() { Key = this.Key, IV = this.Key };
}

class Person

public class Person
{
    public Person() : this("", 0, "")
    {
    }

    public Person(string name, int age, string personalId)
    {
        Name = name;
        Age = age;
        PersonalId = personalId;
    }

    public string Name { get; private set; }
    public int Age { get; private set; }
    public string PersonalId { get; set; }

    public override string ToString()
    {
        return $"이름: {Name}, 나이: {Age}, 주민등록번호: {PersonalId}";
    }
}

class PersonManager

public class PersonManager : ConvertManager<Person>
{
    public PersonManager(string fileName) : base(fileName)
    {
        ConvertEnum = ConvertEnum.Json;
    }

    private EZProtector EZProtector { get; } = new EZProtector("!sZWqrT#");
    public override ConvertEnum ConvertEnum { get; set; }

    public override Person Read()
    {
        var person = FileConverters[(int)ConvertEnum].Deserialize();
        person.PersonalId = EZProtector.Decrypt(person.PersonalId);
        return person;
    }

    public override void Write(Person item)
    {
        item.PersonalId = EZProtector.Encrypt(item.PersonalId);
        FileConverters[(int)ConvertEnum].Serialize(item);
    }
}

Program

class Program
{
    static void Main(string[] args)
    {
        string jsonFileName = @"json.txt";
        string yamlFileName = @"yaml.txt";

        Person first = new Person("최용국", 28, "930000-1111111");

        PersonManager toJson = new PersonManager(jsonFileName);
        toJson.Write(first);
        Console.WriteLine("Json Write Person!!!");
        Console.WriteLine("=======================");
        Console.WriteLine($"Json Read: {toJson.Read()}");

        Person second = new Person("이은혜", 31, "900000-2111111");
        PersonManager toYaml = new PersonManager(yamlFileName);
        toYaml.ConvertEnum = ConvertEnum.Yaml;
        toYaml.Write(second);
        Console.WriteLine("Yaml Write Person!!!");
        Console.WriteLine("=======================");
        Console.WriteLine($"Yaml Read: {toYaml.Read()}");

        Console.ReadLine();
    }
}

결과

json.txt

{"Name":"최용국","Age":28,"PersonalId":"3C3O40to25FAN3QxWYldWQ=="}

yaml.txt

Name: 이은혜
Age: 31
PersonalId: Mwa1iwhsvNdzGT20nJPhtw==
profile
코딩합시다.

0개의 댓글