Skip to content

Design Document

Yoo Jae Beom edited this page Jun 9, 2016 · 47 revisions

Gradle

  • 1 ๋งฅ๋ถ, 1 ๊ธฐ๊ฐ€๋ฐ”์ดํŠธ, 2 ์„œํ”ผ์Šค๋กœ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์˜ ์ฐจ์ด๊ฐ€ ์‹ฌํ•จ
  • ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์ตœ๋Œ€ํ•œ ๋งž์ถ”๊ธฐ ์œ„ํ•ด jetbrain์˜ IntelliJ๋ฅผ ์ด์šฉํ•˜๊ณ  Build tool๋กœ gradle์„ ์„ ํƒ.
  • Gradle

LCS Algorithm

LCS ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์•„๋ž˜์™€ ๊ฐ™์€ ํด๋ž˜์Šค ์ธํ„ฐํŽ˜์ด์Šค ํ•˜์— ํ˜ธ์ถœํ•˜๋„๋ก ์„ค๊ณ„ํ•˜์˜€๋‹ค.

public class LCS<T extends Comparable<T>> implements Diff.Algorithm<T> {
    @Override
    public List<Pair<Integer>> diff(List<T> l, List<T> r) { ... }
    ...
}

LCS ํด๋ž˜์Šค์˜ diff ๋ฉ”์†Œ๋“œ๋Š” List 2๊ฐœ๋ฅผ ๋ฐ›์•„ ๋น„๊ตํ•˜์—ฌ ๊ณตํ†ต๋˜๋Š” ์ˆœ์—ด์˜ ๋ฆฌ์ŠคํŠธ ์ธ๋ฑ์Šค ์Œ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ด๋‹ค. diff ๋ฉ”์†Œ๋“œ์˜ ๊ตฌํ˜„์€ ๋ฆฌ์ŠคํŠธ์˜ ์š”์†Œ๊ฐ€ ๊ณตํ†ต๋˜๋Š” ์š”์†Œ์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋น„๊ต ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ํ•„์š”ํ•˜๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Comparable์˜ ์„œ๋ธŒํƒ€์ž…๋งŒ LCS ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œ์•ฝ์„ ๊ฑธ์–ด๋‘์—ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ดํ›„์— ํ…์ŠคํŠธ๋ฅผ String๋ณด๋‹ค ๋ณต์žกํ•œ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ compareTo ๋ฉ”์†Œ๋“œ๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด LCS ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์•„๋ž˜๋Š” diff ๋ฉ”์†Œ๋“œ์˜ ๋ฐ˜ํ™˜๊ฐ’์— ๋Œ€ํ•œ ์„ค๋ช…์ด๋‹ค.

๊ฐ Pair<Integer>๋Š” LCS ์•Œ๊ณ ๋ฆฌ์ฆ˜ ํ•˜์—์„œ ๋งค์นญ๋˜๋Š” ์ธ๋ฑ์Šค ์Œ์ด๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด List<T> L, R์ด T=Character ์ผ ๋•Œ ์•„๋ž˜ ์ฒ˜๋Ÿผ ๋“ค์–ด์™”๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด
L: ['a', 'b', 'c']
R: ['a', 'c', 'd']
์ด๋ฉด LCS๋Š” ['a', 'c'] ์ด๋‹ค.

LCS์˜ ์ฒซ ์š”์†Œ์ธ 'a'์— ๋Œ€ํ•ด์„œ๋Š” Pair<Integer>๋Š” (0, 0) ์ด๋‹ค.
 > L์—์„œ๋Š” index 0 ์œ„์น˜์— 'a'๊ฐ€ ์žˆ๊ณ , R์—์„œ๋„ index 0 ์œ„์น˜์— 'a'๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
LCS์˜ ๋‘๋ฒˆ์งธ ์š”์†Œ์ธ 'c'์— ๋Œ€ํ•ด์„œ๋Š” Pair<Integer>๋Š” (2, 1) ์ด๋‹ค.
 > L์—์„œ๋Š” index 2 ์œ„์น˜์— 'c'๊ฐ€ ์žˆ๊ณ , R์—์„œ๋Š” index 1 ์œ„์น˜์— 'c'๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

User Requirement

MVC

Our MVC

Sequence Diagram

  • ๋ณด์ด๋Š” ๋ฐ”์™€ ๊ฐ™์ด Controller๊ณผ EditPanel์€ ๊ฐ๊ฐ Merge์™€ JavaFX์— ์ข…์†์ ์ด์ง€๋งŒ ๊ฐ๊ฐ์˜ Model. View ๋ถ€๋ถ„์ด Controller ๋ถ€๋ถ„์— ์ข…์†์ ์ด์ง€๋Š” ์•Š์Œ.

SOLID

๋‹จ์ผ ์ฑ…์ž„ ์›์น™(SRP)

  • ํ•œ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.
  • ์ด์— ๋Œ€ํ•œ ์„ค๋ช…์€ ์•„๋ž˜ Designed to be testable by unit-test tools ๋ถ€๋ถ„์˜ ์„ค๋ช…๊ณผ ์ผ๋งฅ์ƒํ†ตํ•˜๋ฏ€๋กœ ์ƒ๋žตํ•˜๊ฒ ๋‹ค.

๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™(OCP)

  • ์†Œํ”„ํŠธ์›จ์–ด ์š”์†Œ๋Š” ํ™•์žฅ์—๋Š” ์—ด๋ ค ์žˆ์œผ๋‚˜ ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•œ๋‹ค.
package SimpleMerge.util;

public class Pair<T> {
    public final T first, second;
    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Pair<?> pair = (Pair<?>) o;

        if (first != null ? !first.equals(pair.first) : pair.first != null) return false;
        return second != null ? second.equals(pair.second) : pair.second == null;

    }
    @Override
    public int hashCode() {
        int result = first != null ? first.hashCode() : 0;
        result = 31 * result + (second != null ? second.hashCode() : 0);
        return result;
    }
}
  • Generic์œผ๋กœ ๊ตฌํ˜„๋œ Pair๋Š” ๋ชจ๋“ˆ์˜ ๊ธฐ๋Šฅ ํ™•์žฅ์ด๋‚˜ ๋ณ€๊ฒฝ์„ ๋‹ค๋ฅธ ํด๋ž˜์Šค์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ์ง„ํ–‰ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™(LSP)

  • ํ”„๋กœ๊ทธ๋žจ์˜ ๊ฐ์ฒด๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ ์ •ํ™•์„ฑ์„ ๊นจํŠธ๋ฆฌ์ง€ ์•Š์œผ๋ฉด์„œ ํ•˜์œ„ ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
package SimpleMerge.diff;

import SimpleMerge.util.Pair;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class Diff<T extends Comparable<T>> {
    interface Algorithm<T extends Comparable<T>> {
        List<Pair<Integer>> diff(List<T> l, List<T> r);
    }

    private Algorithm<T> algorithm;
    private List<Pair<Integer>> commonLineIndexes;
    private Pair<List<Block>> diffBlocksPair;
    private List<T> list1, list2;

    public Diff() {
        this.algorithm = getDefaultAlgorithm();
    }

    public Diff(List<T> list1, List<T> list2) {
        this.algorithm = getDefaultAlgorithm();
        this.compare(list1, list2);
    }

    private Algorithm<T> getDefaultAlgorithm() {
        return new LCS<T>();
    }

    public void setAlgorithm(Algorithm<T> algorithm) {
        this.algorithm = algorithm;
    }

    public List<Pair<Integer>> compare(List<T> list1, List<T> list2) {
        this.list1 = list1;
        this.list2 = list2;
        commonLineIndexes = algorithm.diff(list1, list2);
        diffBlocksPair = null;
        return commonLineIndexes;
    }

    public Pair<List<Block>> getDiffBlocks() {
        if (diffBlocksPair != null) {
            return diffBlocksPair;
        }
        List<Block> blocks1 = new ArrayList<>(), blocks2 = new ArrayList<>();

        int lastLineIndex1 = -1, lastLineIndex2 = -1;
        for (Pair<Integer> pair : commonLineIndexes) {
            if (lastLineIndex1 + 1 != pair.first || lastLineIndex2 + 1 != pair.second) {
                blocks1.add(new Block(lastLineIndex1 + 1, pair.first));
                blocks2.add(new Block(lastLineIndex2 + 1, pair.second));
            }
            lastLineIndex1 = pair.first;
            lastLineIndex2 = pair.second;
        }
        if (lastLineIndex1 + 1 != list1.size() || lastLineIndex2 + 1 != list2.size()) {
            blocks1.add(new Block(lastLineIndex1 + 1, list1.size()));
            blocks2.add(new Block(lastLineIndex2 + 1, list2.size()));
        }

        diffBlocksPair = new Pair<>(blocks1, blocks2);
        return diffBlocksPair;
    }
}

  • Diff๋Š” ๋ณด์ด๋Š” ๋ฐ”์™€ ๊ฐ™์ด Algorithm์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋‹ค. ์ด Algorithm ๋ถ€๋ถ„์€ Algorithm์„ implement ์ค‘์ธ LCS๋กœ ๊ต์ฒด ํ•˜์—ฌ๋„ ํ”„๋กœ๊ทธ๋žจ์˜ ์ •ํ™•์„ฑ์ด ์œ ์ง€๋œ๋‹ค.

์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™(ISP)

  • ํŠน์ • ํด๋ผ์ด์–ธํŠธ๋ฅผ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ๋ฒ”์šฉ ์ธํ„ฐํŽ˜์ด์Šค ํ•˜๋‚˜๋ณด๋‹ค ๋‚ซ๋‹ค.
  • Interfaces
  • ์ €์žฅ ๋ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋ถ€๋ถ„์˜ ํ•จ์ˆ˜๋ฅผ ๋งก๋Š” EditPanelEventListener์™€ FileChooser์„ ์ด์šฉํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งก๋Š” FileSelector๋ฅผ ๋ถ„๋ฆฌ.

์˜์กด๊ด€๊ณ„ ์—ญ์ „ ์›์น™(DIP)

  • ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ์ง€ ๊ตฌ์ฒดํ™”์— ์˜์กดํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
  • ๋ณธ ์ฝ”๋“œ๋Š” Class๊ฐ„์˜ ์ƒ์†์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋ณธ ์ฝ”๋“œ๋Š” Interface-Class๊ฐ„ ์ƒ์†์„ ์ด์šฉํ•˜์—ฌ View to Controller, Model to Controller ๋ฐฉํ–ฅ์˜ ์˜์กด์„ฑ์„ ์ œ๊ฑฐํ•˜์˜€๋‹ค.
  • ์ž์„ธํ•œ ์˜ˆ์‹œ๋Š” ์ƒ๋‹จ์˜ MVC ์„ค๋ช…๊ณผ ์ผ๋งฅ์ƒํ†ตํ•˜๋ฏ€๋กœ ์ƒ๋žตํ•˜๊ฒ ๋‹ค.

Designed to be testable by unit-test tools

  • UML
  • Mock์ด ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๋‚˜ ๊ธฐ๋Šฅ๋“ค์ด ์„œ๋กœ ์˜์กด์ ์ด์ง€ ์•Š๋„๋ก Interface๋กœ ๋ถ„๋ฆฌ.
  • Merger, LCS ๋“ฑ MVC์—์„œ ๋ชจ๋ธ์€ ์˜์กดํ•˜๋Š” ๊ตฌ์ฒด ํด๋ž˜์Šค๊ฐ€ ์—†์–ด ๋ณ„๋„์˜ Mocking ์—†์ด๋„ ์œ ๋‹› ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • MVC์—์„œ Controller์— ํ•ด๋‹นํ•˜๋Š” EditPanel์€ ์˜์กดํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค(FileSelector)์— ์˜์กดํ•˜๋„๋ก ๋งŒ๋“ค์–ด Mockingํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋˜๋„๋ก ํ•˜์˜€๋‹ค.

Clone this wiki locally