템플릿 메서드 패턴(TemplateMethodPattern)

최용국·2020년 3월 7일
0

디자인패턴

목록 보기
3/6

20-0229 Template Method Pattern

'어떠한 행위' 앞, 뒤 혹은 중간중간에 반복적으로 수행하는 코드들이 존재한다. 그 코드들을 일반화한다. 부모클래스에서 반복적으로 하는 일들을 정의하고 파생클래스에서 '어떠한 행위'를 정의하는 것을 약속한다. 주로 라이브러리를 만들 때 자주 사용하는 패턴이다.

다음 예제는 클래스가 갖고있는 숫자, 혹은 문자열 프로퍼티를 텍스트 파일에 쓰거나 읽을 수 있는 인스턴스가 있고 인스턴스는 파일을 열고 닫을 때 Json 파일로 직렬화, 역직렬화 하는 반복적인 행위는 기반클래스에서 정의하여 반복적인 코드를 줄인다.

이름(string)과 나이(integer)를 프로퍼티로 갖고있는 Person 클래스가 있고 우선 Person 클래스를 문자열만 쓸 수 있는 인스턴스를 통해 이름을 쓰고, 숫자만 쓸 수 있는 인스턴스를 통해 나이를 쓰는데 숫자는 30 미만만 쓰도록 한다. 마지막으로는 Json으로 쓰여진 파일을 열어서 문자, 숫자만을 각각 읽어온다.

class BaseTexter

public static int Seq { get; private set; } = 1;

public abstract class BaseTexter<T, U> where T : class
{
    public string TargetFile
    {
        get; private set;
    }
    public BaseTexter(string targetFile)
    {
        this.TargetFile = targetFile;
    }

    public void Write(T target)
    {
        var values = GetValues(target);
        if (values.Count == 0)
            return;

        var serialize = JsonConvert.SerializeObject(values);

        JObject root = ReadAll(this.TargetFile);

        root.Add(string.Format("{0:D2}", Seq), JToken.Parse(serialize));

        using (FileStream fileStream = File.Create(TargetFile))
        {
            using (JsonTextWriter writer = new JsonTextWriter(new StreamWriter(fileStream)))
            {
                root.WriteTo(writer);
            }
        }

        Seq++;
    }

    public static JObject ReadAll(string targetFile)
    {
        if (!File.Exists(targetFile))
            using (File.Create(targetFile)) { }


        using (FileStream fileStream = File.Open(targetFile, FileMode.Open, FileAccess.Read))
        using (StreamReader reader = new StreamReader(fileStream))
        {
            return JsonConvert.DeserializeObject<JObject>(reader.ReadToEnd()) ?? new JObject();
        }

    }

    public Dictionary<string, U> GetValues(T target)
    {
        Dictionary<string, U> nameValuePair = new Dictionary<string, U>();
        foreach (var prop in target?.GetType().GetProperties())
        {
            if (prop.PropertyType == ValueType())
            {
                var value = (U)prop.GetValue(target);
                if (Where(value))
                {
                    nameValuePair.Add(prop.Name, value);
                    Console.WriteLine($"{prop.Name}: {value} add");
                }
                else
                    Console.WriteLine($"{prop.Name}: {value} skip");
                
            }
        }

        return nameValuePair;
    }

    public void Read()
    {
        JObject root = ReadAll(this.TargetFile);

        foreach(var childProp in root.Properties())
        {
            foreach(var values in childProp.Children())
            {
                
                foreach (var value in values.ToObject<JObject>().Properties())
                {
                    if(TypeCompare(value.Value.Type))
                        Console.WriteLine($"{childProp.Name}: prop: {value.Name} value: {value.Value}");
                }
            }
        }

    }

    public abstract Type ValueType();

    public abstract bool Where(U value);

    public abstract bool TypeCompare(JTokenType type);

}

class StringTexter

public class StringTexter<T> : BaseTexter<T, string> where T : class
{
    public StringTexter(string targetFile) : base(targetFile)
    {
    }

    public override bool TypeCompare(JTokenType type) => type == JTokenType.String;
    public override Type ValueType() => typeof(string);

    public override bool Where(string value) => true;
}

class IntTexter

public class IntTexter<T> : BaseTexter<T, int> where T : class
{
    public IntTexter(string targetFile) : base(targetFile)
    {
    }

    public override bool TypeCompare(JTokenType type) => type == JTokenType.Integer;

    public override Type ValueType() => typeof(int);

    public override bool Where(int value)
    {
        if (value < 30)
            return true;
        else
            return false;
    }
}

class Person

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

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

Program

static void Main(string[] args)
{

    StringTexter<Person> stringTexter = new StringTexter<Person>(@"C:\CSharp\Study\200224\Pattern\TemplateMethod\jsonText.txt");
    IntTexter<Person> intTexter = new IntTexter<Person>(@"C:\CSharp\Study\200224\Pattern\TemplateMethod\jsonText.txt");

    List<Person> people = new List<Person>()
    {
        new Person("최용국", 28),
        new Person("이은혜", 31),
        new Person("안광필", 35),
        new Person("이정선", 29)
    };
    
    foreach(var person in people)
    {
        stringTexter.Write(person);
    }
    foreach(var person in people)
    {
        intTexter.Write(person);
    }


    Console.WriteLine("================integer============");

    intTexter.Read();

    Console.WriteLine("================string============");

    stringTexter.Read();

    
}

결과


profile
코딩합시다.

0개의 댓글