Techit 4th 3rd

Huisu·2023년 5월 10일
0

Techit

목록 보기
2/42
post-thumbnail

Algorithm

Diamond

indexspacestart
031
123
215
307
415
523
631

증가하는 부분과 감소하는 부분을 나누기 위해서 전체 높이를 나누기 2 한 pivot 값이 필요하다. pivot을 기준으로 피라미드 모양과 역피라미드 모양이 합쳐져 있다고 생각하면 된다.

피라미드 모양에서 공백은 3 → 2 → 1 순서로 공차가 1이고 첫째항이 pivot이기 때문에 pivot - i 로 설정하면 된다. 반면 별 모양은 1 → 3 → 5 → 7 순서로 공차가 2이고 첫째항이 1이기 때문에 2 * i + 1 로 설정한다.

역피라미드 모양에서 공백은 3 → 2 → 1 순서로 공차가 -1이고 첫째항은 h 이다. index가 pivot 바로 다음 값이기 때문이다. 하지만 아래 코드에서는 뺄셈을 이용해서 i - pivot으로 설정해 주었다. 별의 개수는 5 → 3 → 1로 공차가 -2이고 첫째항이 13이다. 13을 가지고 있는 변수로 생각해 보면 2 pivot + h 로 치환할 수 있다. 따라서 `-2 i + pivot * 2 + h` 로 설정해 주었다.

public class DrawDiamond {
    public static String getRepeatedSymbol(String symbol, int repeatNum) {
        return symbol.repeat(repeatNum);
    }

    public static void main(String[] args) {
        int h = 7;
        int pivot = h / 2;
        for (int i = 0; i < h; i++) {
            if(i <= pivot) {
                //System.out.printf("%d %d\n", pivot - i, 2 * i + 1);
                System.out.printf("%s%s\n", getRepeatedSymbol(" ", pivot - i), getRepeatedSymbol("*", 2 * i + 1));
            } else {
                //System.out.printf("%d %d\n", i - pivot, -2 * i + 2 * pivot + h);
                System.out.printf("%s%s\n", getRepeatedSymbol(" ", i - pivot), getRepeatedSymbol("*", -2 * i + 2 * pivot + h));
            }
        }
    }
}
   *
  ***
 *****
*******
 *****
  ***
   *

JAVA

Interface

Interface
자식 클래스가 여러 부모 클래스를 상속받을 수 있다면, 다양한 동작을 수행할 수 있다는 장점을 가지게 될 것이다. 하지만 클래스를 이용하여 다중 상속을 할 경우 메소드 출처의 모호성 등 여러 가지 문제가 발생할 수 있어 자바에서는 클래스를 통한 다중 상속은 지원하지 않는다. 하지만 다중 상속의 이점을 버릴 수는 없기에 자바에서는 인터페이스라는 것을 통해 다중 상속을 지원하고 있다.
인터페이스(interface)란 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미한다. 자바에서 추상 클래스는 추상 메소드뿐만 아니라 생성자, 필드, 일반 메소드도 포함할 수 있다. 하지만 인터페이스(interface)는 오로지 추상 메소드와 상수만을 포함할 수 있다.

피라미드 도형을 출력하는 기능은 1. 콘솔에 출력 2. 알림 전송 3. 파일에 저장 등 세 개가 있다. 이때 기존 로직은 변화를 주지 않고 기능을 확장하고 싶을 때 Interface를 사용한다. 기존 코드는 main method에서 hello를 출력하기 위해 메소드만 분리하는 형식을 가졌다.

public class HelloPrinter {
    public void print(String message) {
        System.out.println(message);
    }
    public static void main(String[] args) {
        HelloPrinter hp = new HelloPrinter();
        hp.print("Hello");
    }
}

만약 여기에 같은 문자열을 반복하는 repeat 기능이 추가된 메소드를 하나 추가한다고 생각해 보자. 코드는 다음과 같다.

public class HelloPrinter {
    // 파일에도 저장하고 싶고 출력도 하고 싶음
    public void print(String message) {
        System.out.println(message);
    }

    public void repeatMessage(int n, String message) {
        for (int i = 0; i < n; i++) {
            System.out.println(message);
        }
    }

    public static void main(String[] args) {
        HelloPrinter hp = new HelloPrinter();
        hp.print("Hello");
        hp.repeatMessage(5, "Hi");
    }
}

이때 Printer라는 기능을 마치 컴퓨터에 블루투스 스피커를 연결했다 뺐다 하는 것처럼 사용하기 위해 Interface로 구현해 준다.

package week4.day3.inter;

public interface Printer {
    void print(String message);
}

콘솔에 출력하는 인터페이스 구현체를 만들면 다음과 같다. 추상 클래스를 상속할 때는 extend를 사용했지만 인터페이스를 받을 때는 implements를 사용한다.

public class ConsolePrinter implements Printer {
    @Override
    public void print(String message) {
        System.out.println(message);
    }
}

이후 인터페이스 선언하는 부분에 정확하게 어떤 구현체를 사용할 것인지 선언한 뒤, 해당 구현체의 기능을 사용한다.

public class HelloPrinter {
    Printer printer = new ConsolePrinter();
    // 파일에도 저장하고 싶고 출력도 하고 싶음

    public void repeatMessage(int n, String message) {
        for (int i = 0; i < n; i++) {
            printer.print(message);
        }
    }

    public static void main(String[] args) {
        HelloPrinter hp = new HelloPrinter();
        hp.repeatMessage(5, "Hi");
    }
}

생성자로 DI 받도록 구조를 변경해 준다.

public class HelloPrinter {
    Printer printer;
    // 파일에도 저장하고 싶고 출력도 하고 싶음

    public HelloPrinter(Printer printer){
        this.printer = printer;
    }
    public void repeatMessage(int n, String message) {
        for (int i = 0; i < n; i++) {
            printer.print(message);
        }
    }

    public static void main(String[] args) {
        HelloPrinter hp = new HelloPrinter(new ConsolePrinter());
        hp.repeatMessage(5, "Hi");
    }
}

File에 쓰는 기능으로 프린트 인터페이스를 변형하기 위해 FilePrinter 클래스를 하나 생성해 준다.

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class FilePrinter implements Printer{
    @Override
    public void print(String message) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("./hello.txt"));
        bw.append(message);
        bw.flush();
        bw.close();
    }
}

이후 FilePrinter로 구현체를 바꿔서 출력해 본다.

import java.io.IOException;

public class HelloPrinter {
    Printer printer;
    // 파일에도 저장하고 싶고 출력도 하고 싶음

    public HelloPrinter(Printer printer){
        this.printer = printer;
    }
    public void repeatMessage(int n, String message) throws IOException {
        for (int i = 0; i < n; i++) {
            printer.print(message);
        }
    }

    public static void main(String[] args) throws IOException {
        HelloPrinter hp = new HelloPrinter(new FilePrinter());
        hp.repeatMessage(5, "Hi");
    }
}

Strategy Pattern
Console Printer를 사용할 것인지 File Printer를 사용할 것인지 패턴을 나누는 디자인 방식이다.

DrawShape Example

도형을 만들기 위해 총 몇 줄이고, 몇 번째 줄인지를 넘겨 주면 해당 행에 걸치는 문자열을 반환해 주는 makeALine 함수가 필요하다. 하지만 이는 피라미드 모양인지, 삼각형 모양인지에 따라 그 리턴값이 다르다. 따라서 makeALine을 인터페이스로 설정해 준다.

public interface ShapeDrawer {
    public abstract String makeALine(int h, int i);
}

그리고 피라미드를 만들 건지 삼각형을 만들 건지에 따라 구체적인 구현체를 나눠 준다.

public class PyramidDrawer implements ShapeDrawer{
    @Override
    public String makeALine(int h, int i) {
        return String.format("%s%s", " ".repeat(h - i), "*".repeat(2 * i - 1));
    }
}
public class TriangleDrawer implements ShapeDrawer{
    @Override
    public String makeALine(int h, int i) {
        return String.format("%s", "*".repeat(i));
    }
}

이후 main Method가 있는 파일에서 반복문을 돌며 줄마다 출력하는 함수를 가진 클래스를 생성해 준다. 이때 클래스는 ShapeDrawer라는 멤버 변수를 가지고 있어서, 생성자로 생성할 때마다 피라미드를 만들 건지 삼각형을 만들 건지 다양하게 생성할 수 있도록 한다.

public class DrawStar {

    ShapeDrawer shapeDrawer;

    public DrawStar(ShapeDrawer shapeDrawer) {
        this.shapeDrawer = shapeDrawer;
    }

    public void printShape(int h) {
        for (int i = 1; i <= h; i++) {
            System.out.printf("%s\n", this.shapeDrawer.makeALine(h, i));
        }
    }

    public static void main(String[] args) {
        DrawStar pyramid = new DrawStar(new PyramidDrawer());
        pyramid.printShape(8);
        DrawStar triangle = new DrawStar(new TriangleDrawer());
        triangle.printShape(8);
    }
}

0개의 댓글