[JAVA] 객체지향 프로그래밍

- 16 mins

주의

아래 내용은 비정기적으로 수정됩니다.

Description

기초적인 JAVA 문법만 아는 초보들을 위한 객체지향설계개념코딩컨벤션.

INDEX

  1. Java Coding Convention
  2. 자바는 객체지향언어다
  3. Design Pattern






1. Java Coding Convention

Oracle의 공식 Java 8 Coding Convention을 참조하였다.

Java 코드 작성 시 어떤 규칙을 지켜야 하는지 Oracle의 공식 가이드에 자세히 설명되어있다. 평소에 모든 가이드 내용을 숙지하고 있기 어렵기 때문에 자주 틀리는 공백(White Space) 규칙을 정리하겠다.

Blank Lines

공백행은 논리적으로 관련된 코드 섹션을 나누어 가독성을 향상 시킴

빈 줄 두 칸
package test; // 패키지 영역
// Blank Line
import java.util.Scanner; // import 영역
// Blank Line
public Test{ // class 영역
  ...
}
// Blank Line
class Test {
}
// Blank Line
inteface Test2 {
}
빈 줄 한 칸
public void method1() {
  ...
}
// Blank Line
public void method2() {
  ...
}
public void method1() {
  int sum = 0;
  // Blank Line
  for (int i = 0; i < 10; i++) {
    sum += i;
  }
}
// Blank Line
/*
* 블록코멘트
*/
// Blank Line
// 한 줄 주석
public void mehtod(int n) {
  int sum = 0;
  // Blank Line
  for (int i=0; i < n; i++) {
    sum += i;
  }
  // Blank Line
  for (int i=n; i > 0; i--) {
    sum -= i;
  }
}

Blank Spaces

while (true) {
    ...
}

Tip: 공백은 메서드 이름과 여는 괄호 사이에 사용하면 안됩니다. ex) public void method() {}

public int add(int a, int b) {
  return a + b;
}
int a = 1;
int b = 2;

// 이진연산자
int result = a + b;
// 단항연산자
a++;
for (expr1; expr2; expr3)
double a = 1.0;
int result = (int) a;

2. 자바는 객체지향언어다

스프링 입문을 위한 자바 객체지향의 원리와 이해를 참고하여 작성

*"중복은 해악이다."*

2-1 Class vs Object

클래스 vs 객체

  1. 객체 란 유일무이하게 식별이 가능한 것을 말한다.
    • 세상에 존재하는 모든 ‘사물’.
    • ‘실체’하는 유일한 것
    • ‘속성’과 ‘행위’를 갖는 것
  2. 클래스 란 객체들의 분류이다.
    • ‘분류’
    • ‘개념’

보통 클래스와 객체의 차이를 답하라 하면 많은 사람들이 붕어빵붕어빵틀의 예시를 든다. 보통 Class를 생성하여 Object를 만들 수 있다 생각하기 때문에 붕어빵틀붕어빵을 만들 수 있다는 것이다.

간단히 코드로 옮겨보면

붕어빵틀 붕어빵 = new 붕어빵틀();

붕어빵틀붕어빵을 만들었는데 타입이 붕어빵틀이란 것은 말이 안되는 코드다. 붕어빵틀붕어빵에 대한 클래스 가 아니라 붕어빵에 대한 팩토리클래스 이다. (팩토리 클래스는 다른 객체를 생성하는 클래스다.)

붕어빵 크림붕어빵 = new 붕어빵();

이렇게 되어야 한다.

객체 식별하기

객체와 클래스를 구별짓는 가장 중요한 질문은 이것이 ‘분류’인가 ‘사물’인가이다.

객체는 유일한 identity를 갖는다.

나이, 제조년월을 물었을 때 답할 수 있다면, 객체. 없다면 클래스이다.

피켜스케이터 김연아 = new 피겨스케이터();
// 김연아 -> 객체

자동차 아버지의소나타 = new 자동차();
// 자동차 -> 클래스
// 아버지의소나타 -> 객체

고양이 냐옹이 = new 냐옹이();
// 고양이 -> 클래스
// 냐옹이 -> 객체

때문에 객체를 구현할 때 이름이 중요하다. 객체는 분류의 명칭을 가지면 안된다.

Student student = new Student(); // x
Student james = new Student(); // o

2-2 객체지향 4대 특성

2-3 객체지향 설계 5원칙

SOLID

2-4 Tip

// carsNums는 cars.size()로 구할 수 있기 때문에 중복이다.
public Cars{
  private List<Car> cars; // Car 객체들이 저장될 인스턴스 변수
  private int carsNums; // 저장된 Car 객체들의 갯수

  ...
}

다형성

// 변경에 유연하게 대응할 수 있다.
class A {
  private B; // 여기서 B를 비슷한 기능을 가진 다른 객체로 바꾸어 사용이 가능
}
// 기능을 '확장'하기위해 사용한다.
class A {
  protected String name; // 상속받은 자식까지만 접근할 수 있게

  A(String name) {
    this.name = name;
  }

  public void print() {
    System.out.println(this.name + "는 A꺼다.");
  }

}

class B extend A {

  B(String name) {
    super(name);
  }

  @Override
  public void print() {
    System.out.println(this.name + "는 B꺼다.");
  }

}

중복되는 부분이 있을 때 중복되는 부분을 abstract class의 메소드로 추출한다. 추상클래스의 역할은 인터페이스와 실제 구현할 클래스 사이에 중복되는 메소드를 제거하는 역할을 한다.

중복을 제거한 클래스의 계층
inteface A
abstract class B implements A
class C extend abstract B
List<Integer> aList = new ArrayList<>();
List<Integer> lLIst = new LinkedList<>();

// 두 List 타입의 객체를 서로 다른 클래스로 구현하였으나 외부에 노출되는 기본 행위(메소드)는 동일하여 사용자는 어떤식으로 구현되어있는지 신경쓰지 않아도 된다.
aList.add(1);
lList.add(1);

3. Design Pattern

3-1 MVC 패턴

재사용성을 높여 비용을 절감하는 것. 특히 모델(핵심 로직)을 재사용하기 위해. 뷰와 컨트롤러만 바꾸어서 Web -> App -> API 등으로 사용할 수 있다. 핵심로직은 잘 바뀌지 않는다. 제일 많이 변경되는 곳은 View다.

3-2 Builder 패턴

객체 생성을 위해 사용하는 디자인 패턴

생성자의 인자가 많을 경우 Builder 패턴 적용을 고려하라. https://cheese10yun.github.io/intellij-builder-pattern/ https://johngrib.github.io/wiki/builder-pattern/

기존의 생성자를 이용한 방식

필수 인자 외에 선택인자들을 받아 객체를 만들 때, 코드 수정이 어려움. 인자 수가 많아지면 인자의 위치가 어떤 필드인지 알기 힘들다.

public class Person {
    private final String name; // 필수
    // 선택
    private int age;
    private int height;
    private String university;
    private String[] hobby;

    public Person(String name) {
        this.name = name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name, int age, int height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    public Person(String name, int age, int height, String university) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.university = university;
    }

    public Person(String name, int age, int height, String university, String[] hobby) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.university = university;
        this.hobby = hobby;
    }
}

Java Bean 방식

setter 메소드를 사용하여 어떤 인자를 추가하는지 알 수 있다. 그리고 생성자가 여러개일 필요가 없어서 좋다. 하지만 한번에 객체를 생성할 수 없고, 객체를 만들고 나서 계속 값을 추가해 주어야한다. ImmutableObject(변경 불가능한 객체)를 만들 때 쓸 수 없는 방식이다.

public class Person {
    private String name;
    private int age;
    private int height;
    private String university;
    private String[] hobby;

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public String getUniversity() {
        return university;
    }

    public void setUniversity(String university) {
        this.university = university;
    }

    public String[] getHobby() {
        return hobby;
    }

    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }
}

Builder 패턴을 쓸 경우

한번에 객체를 생성할 수 있다(chaining이 가능하기 때문에). 또한 인자를 추가하기 쉽고, 추가할 때 마다 인자가 어떤 인자인지 확실하게 구별할 수 있다. setter메소드를 쓰지 않기때문에 ImmutableObject 만들 수 있다.

build()를 호출할 때 값의 유효성 검사를 전담시킬 수 있다.

Person sehun = Person.PersonBuilder
        .aPerson()
        .withName("sehun")
        .withAge(28)
        .withHeight(178)
        .withUniversity("SKHU")
        .withHobby(new String[]{"sleep", "music"})
        .build();
// inner builder (클래스 내부에 inner class로 빌더를 가질 경우)
public class Person {
    private final String name;
    private int age;
    private int height;
    private String university;
    private String[] hobby;

    public Person(String name) {
        this.name = name;
    }

    public static final class PersonBuilder {
        private String name;
        private int age;
        private int height;
        private String university;
        private String[] hobby;

        private PersonBuilder() {
        }

        public static PersonBuilder aPerson() {
            return new PersonBuilder();
        }

        public PersonBuilder withName(String name) {
            this.name = name;
            return this;
        }

        public PersonBuilder withAge(int age) {
            this.age = age;
            return this;
        }

        public PersonBuilder withHeight(int height) {
            this.height = height;
            return this;
        }

        public PersonBuilder withUniversity(String university) {
            this.university = university;
            return this;
        }

        public PersonBuilder withHobby(String[] hobby) {
            this.hobby = hobby;
            return this;
        }

        public Person build() {
            Person person = new Person(name);
            person.age = this.age;
            person.height = this.height;
            person.university = this.university;
            person.hobby = this.hobby;
            return person;
        }
    }
}

Sehun Kim

Sehun Kim

하다보니 되더라구요.

comments powered by Disqus
rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora