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 ##MVC?

  • 현 프로젝트는 MVC 패턴을 적용시키기에는 적합하지 않다고 판단.
  • Model이 매우 작음. 실질적으로 View, Controller 부분에서 대부분 처리가 가능함.

##Our MVC Sequence Diagram

  • 보이는 바와 같이 Controller과 EditPanel은 각각 Merge와 JavaFX에 종속적이지만 각각의 Model. View 부분이 Controller 부분에 종속적이지는 않음.

#SOLID ##단일 책임 원칙(SRP)

  • 한 클래스는 하나의 책임만 가져야 한다.

##개방-폐쇄 원칙(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)

  • 프로그래머는 추상화에 의존해야지 구체화에 의존하면 안된다.
    1. UML 사진을 올려 상속을 받지 않고 추상화에 의존했음을 보인다

#Designed to be testable by unit-test tools

  • 개발이 끝난 뒤 uml이나 code 사진을 부착할 예정
  • Mock이 테스트를 할 수 있도록 각각의 컴포넌트나 기능들이 서로 의존적이지 않도록 Interface로 분리.
  • 추가 내용을 입력해주세요.

Clone this wiki locally