객체 지향 프로그래밍

컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러개의 독립된 단위.

"객체" 들의 모임으로 파악하고자 하는 것

각각의 객체는 메세지를 주고받고, 데이터를 처리할 수 있다. = 협력

 

또 객체 지향 프로그래밍은 프로그램을 유연하고 변경이 유용하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다. 

유연 변경? 다시 말해 레고 블럭 조립하듯 컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있는 방법

이것이 다형성(Polymorphism)이라고 한다. 

 

객체 지향의 가장 중요한 개념 중 하나는 다형성이다. 

다형성은 역할과 구현으로 구분을 한다.  역할은 인터페이스고 구현은 그 인터페이스를 구현한 객체라고 보면된다!

 

 

 

다형성을 운전자-자동차로 예를 들어보자

 

운전자(역할)는 자동차(역할)를 운전한다 

'자동차 역할'을 '자동차 구현'으로 분리해보면 K3, 아반떼, 테슬라..(인스턴스)

'자동차 구현' 이 바뀌어도 운전자는 역시나 운전할 수 있다.

이 말은 즉슨, 운전자는 자동차 인터페이스 즉 '자동차 역할' 에 대해서만 의존을 하고있다. 

'자동차 구현' 처럼 다른 대상으로 변환이 가능하고(새로운 기능들 제공) 무한확장이 가능하나 '자동차 역할'에 영향을 주지 않으며 운전자(클라이언트)는 바꿀 필요가 없다. 

이것이 바로 '유연하고 변경용이하다' 라고 할 수 있다. 

 

 

정리
역할과 구현을 분리
  • 역할과 구현으로 구분하면 유연해지며 변경이 편리해진다 
  • 클라이언트는 대상의 역할(인터페이스)만 알면 된다. 
  • 클라이언트는 구현 대상의 내부 구조를 몰라도 된다 (자동차 디테일 구조를 몰라도 됨 )
  • 클라이언트는 내부 구조가 변경되어도 영향을 받지 않는다. (엑셀, 브레이크만 알아도 된다)
  • 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다 (K2 에서 테슬라로 바꿔도 운전자는 영향X) 

핵심은 역할이 중요하다! 

  • 자바 언어
    • 역할 = 인터페이스
    • 구현 = 인터페이스를 구현한 클래스, 구현 객체

객체를 설계할 때 역할과 구현을 명확히 분리 

객체 설계시 역할(인터페이스)를 먼저 부여하고 , 그 역할을 수행하는 구현 객체 만들기

 

 

이론으로는 알겠는데 이제 코드로 예를 들어보자 

 

  • 역할 : MemberRepository 인터페이스
  • 구현 : MemoryMemberRepository 클래스, JdbcMemberRepository 클래스
    • 인터페이스를 구현한 객체 인스턴스

 

우선 MemberService 에서 save()를 호출하면 

MemberRepository 인터페이스를 구현 한  MemoryMemberRepository 클래스는 save() 메서드를 호출된다.

 

MemoryMemberRepository 클래스는 save()를 오버라이딩을 하여 자체적인 동작(구현)을 제공할 수 있다. 

다시 말해 MemberService 는 MemberRepository 인터페이스를 통해 MemoryMemberRepository 클래스의 메서드 save() 를 호출할 수 있다.  

  • MemberService > MemberRepository > MemoryMemberRepository 의 save()호출

 

 

[MemberService]

public interface MemberService {

	//회원 가입
	void join(Member member);
	
	//회원 조회
	Member findMember(Long memberId);
}

 

 

 

[MemberRepository]

public interface MemberRepository {

	//회원 저장
	void save(Member member);
	
	//회원 id를 찾는기능
	Member findById(Long memberId);
}

 

[MemoryMemberRepository]

@Component
public class MemoryMemberRepository implements MemberRepository{ //MemberRepository를 구현

	
	private static Map<Long, Member> store = new HashMap<>();
	
	//MemberRepository의 save()를 재정의(오버라이딩)
	@Override
	public void save(Member member) {
		store.put(member.getId(), member);
	}

	@Override
	public Member findById(Long memberId) {
		return store.get(memberId);
	}

}

 

 

여기서 다형성의 본질을 배울 수 있다. 

  • 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.
    • MemberService 는 MemberRepository 인터페이스의 메서드를 호출하기 때문에 인터페이스에만 의존한다.
    • 의존한다는 뜻은 "내가 쟤를 알고 있다" 라는 말
    • MemberService 는 MemoryMemberRepository 의 구체적인 코드를 몰라도 된다. 
  • 인터페이스를 구현한 객체 인스턴스( MemoryMemberRepository )를 실행시점에서 유연하게 변경할 수 있다.   
    • 구현체( MemoryMemberRepository ) 변경가능 = JdbcMemberRepository 클래스를 사용해도 된다. 

 

대신 인터페이스가 깨지면 모두 깨짐

그래서 인터페이스를 안정적으로 잘 설계하는게 가장 중요하다!

 

 

 

스프링과 객체 지향

  • 스프링은 다형성이 가장 중요하다.
  • 스프링을 사용하면 구현을 편리하게 변경할 수 있다.
  • 다형성을 극대화해서 이용할 수 있게 도와주고 다형성을 편리하게 사용할 수 있도록 지원하는 개념이라고도 할 수 있다.
  • 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용하여 역할과 구현을 편리하게 다룰 수 있도록 지원한다

 

 

 

SOLID 

: 좋은 객체 지향 설계의 5가지 원칙

  • SRP (Single responsibility principle) 단일 책임 원칙
    • 한 클래스는 하나의 책임만 가진다 
    • 변경이 있을 때 파급효과가 작으면 단일 책임 원칙을 잘 따른것 
    • ex) UI 변경, 객체 생성과 사용을 분리
  • OCP (Open / cloased principle) 개방-폐쇄 원칙
    • 소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀 있어야한다 (코드에 변경할 필요없이 기능추가 가능)
    • 역할과 구현의 분리같은 거라고 생각하면된다
    • 다형성을 활용해보면 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현 추가
      • 새로운 JdbcMemberRepository 클래스 만들어서 MemoryMemberRepository 클래스를 변경
      • 기존 코드를 변경을 하지않은 것! 
    • 클라이언트 코드인 MemberService 에서 구현 클래스를 직접 선택시
      • MemberRepository m = new MemoryMemberRepository(); //기존코드
      • MemberRepository m = new JdbcMemberRepository(); //변경코드
      • OCP 위반 (코드가 변경됐기 때문)
    • OCP 원칙을 지키려면 다형성 만으로 지킬 수 없고 DI, IOC 컨테이너가 필요함 
  • LSP (Liskov substitution principle) 리스코프 치환 원칙
    • 자동차가 엑셀 구현(앞으로 간다의 기능) 하도록 구현한다. 
      • 뒤로가는 기능을 만들면 리스코프 치환 원칙을 위반
  • ISP (Interface segregation principle) 인터페이스 분리 원칙
    • 특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다
      • 자동차 인터페이스 => 운전 인터페이스 , 정비 인터페이스로 분리하면
      • 사용자 클라이언트 => 운전자 클라이언트 , 정비사 클라이언트로 분리가 가능하다
      • 이렇게 분리하면 정비 인터페이스를 변경해도 운전자 클라이언트에 영향을 주지 않아서 인터페이스가 명확하고 대체 가능성이 높아진다. 
  • DIP (Dependency inversion principle) 의존관계 역전 원칙
    • 추상화에 의존하고 구체화에 의존하면 안된다 
    • 다시 말해 클라이언트 코드가 구현 클래스에 의존하지 않고 인터페이스에 의존해야한다 (객체도 인터페이스에 의존하면 유연하게 구현체 변경가능)
    • 예를들어 운전자는 자동차의 역할만 알면되지 k3에 대해 알 필요가 없다.
    • DIP원칙을 지키려면 다형성 만으로 지킬 수 없고 DI, IOC 컨테이너가 필요함 
    • OCP 처럼 클라이언트 코드인 MemberService 에서 구현 클래스를 직접 선택시
      • MemberRepository m = new MemoryMemberRepository(); //기존코드
      • MemberRepository m = new JdbcMemberRepository(); //변경코드
      • MemberRepository 인터페이스에 의존하나 구현클래스까지 동시에 의존한다.
      • DIP위반 (구체화에 의존했기 때문)

 

 

 

정리 

: 다형성만으로는 OCP, DIP 를 지킬 수 없다.

 

 

어떻게 해결? 스프링으로! 

  • 스프링은 DI , DI 컨테이너 제공으로 다형성 + OCP,DIP 를 가능하게 지원한다.
  • 클라이언트 코드 변경없이 기능확장

 

 

 

반응형
LIST

 

 

 

버전 에러가 뜬다

 

 

첫번째 방법 

1.8버전으로 수정해보기!

 

이클립스 설치 폴더에 eclipse.ini 파일이 있다 들어가면 

-Dosgi.requiredJavaVersio=11 

-Dosgi.requiredJavaVersio=11

을 찾아서 1.8로 수정하고 

 

-vm
C:\Program Files\Java\jdk-11\bin\javaw.exe

도 추가해준다.

java.exe와 차이는 콘솔 유무라나..?

 

 

 

그래도 안된다 

 

 

 

두번째 방법

환경변수를 확인해보자

 

eclipse.ini 파일에 다시 11버전으로 원상복귀 시키고 

-vm
C:\Program Files\Java\jdk-11\bin\javaw.exe 는 그대로 추가 

 

환경변수를 확인했더니 1.8로 돼있던것! 

JAVA_HOME 을 C:\Program Files\Java\jdk-11 로 수정해주자! 

 

이클립스 에러가 안뜸! 성공

 

반응형
LIST

객체의 참조와 테이블의 외래 키의 매핑

객체의 연관관계는 참조 > .getTeam()

테이블은 연관관계는 외래키

 

용어 이해

방향 : 단방향, 양방향

다중성 : 다대일, 일대다, 일대일, 다대다

연관관계의 주인 : 객체 양방향 연관관계는 관리가 필요

 

객체지향의 오해와 진실/오브젝트 책 추천

 

 

 

테이블 연관관계에서 다대일(N:1) 관계일 경우 

 

 

하나의 팀에서 여러명의 멤버일때 

팀:멤버 = 1:N 

 

  • Member 클래스에서 Team 클래스를 쓸 때 연관관계는 @ManyToOne
  • Member 객체는 MEMBER 테이블의 FK와 조인해야해서  @JoinColumn(name = "TEAM_ID")
@Entity
public class Member {

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

}

 

 

 

 

 

가장 중요!

양방향 연관관계와 연관관계의 주인

 

 

 

양방향 참조할 수 있는것 

 

보다시피 위에서 정리했던 단방향 연관관계의 테이블과 전혀 변화가 없다. 

MEMBER의 FK와 TEAM의 PK와 조인만 하면 양쪽으로 연관관계를 다 알 수 있다.

문제는 객체! 멤버에서 팀은 갈수 있으나 팀에서 멤버는 못갔다 

그래서 팀에 List members를 넣어준다. 

 

 

 

mappedBy = "team"

 

팀과 멤버는 일대다 관계여서 @OneToMany 

@Entity
public class Team {

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

}

 

mappedBy = "team" 는 멤버 클래스의 team 과 매핑이 되어있는 상태이다 라는 뜻

@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;

 

 

 

 

객체와 테이블이 관계를 맺는 차이

  • 객체 연관관계 (2개) : 사실 양방향 관계가 아닌 서로 다른 단방향 관계 2개
    • 멤버 > 팀 = 1개 (단방향)
    • 팀 > 멤버 = 1개 (단방향)
  • 테이블 연관관계 = 1개 
    • 멤버 <> 팀 = 1개 (양방향 , 사실 방향이 없음)

 

 

 

단방향 연관관계를 2개 만들어서 객체를 양방향으로 참조한 것!

객체의 연관관계

 

 

 

외래키 하나로 양방향 연관관계를 가짐 (양쪽으로 조인가능)

테이블의 연관관계

 

 

연관관계의 주인

 

양방향 매핑에서 나옴

객체 두 관계 중 하나를 연관관계의 주인으로 지정해야하는데 

주인만이 외래 키를 관리(등록, 수정) 하고 주인이 아니면 읽기만 가능

 

주인은 mappedBy 속성 사용할 수 없으며 주인이 아니면 mappedBy 속성으로 주인을 지정해야한다.  

@Entity
public class Team {

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

}


mappedBy = "team"라는 의미는 'team에 의해 관리가 된다'는 뜻
team은 Member 클래스에 있으며
결론적으로 여기에서 값을 수정해봤자 소용이 없고 조회만 가능하다. 
@Entity
public class Member {

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

}

여기는 연관관계의 주인

 

 

결론

누구를 주인으로 하는가?  외래 키가 있는 곳을 주인으로 정해야한다!!

다대일 은 외래키가 있는 곳이 '다' 고 외래키가 없는곳이 '일'이다

다시 말해, '다' 쪽이 외래키가 있어서 무조건 연관관계의 주인이다.

 

그러면 덜 헷갈린다

 

반응형
LIST
엔티티 매핑 
  • 객체와 테이블 매핑 : @Entity, @Table
  • 필드와 컬럼 매핑 : @Column
  • 기본 키 매핑 : @Id
  • 연관관계 매핑 : @ManyToOne, @JoinColumn

 

 

이어서 기본키 매핑과 연관관계 매핑 정리를 해보자

 

@Entity
public class Member {
	@Id
	private String id;
    
	@Column(name="name", unique=false)
	private String username;
    
    ..
}

3. 기본 키 매핑 (@Id, @GeneratedValue)

 

3-1. @Id

: 직접 할당

 

 

3-2. @GeneratedValue

: 자동 생성

* id 값을 넣으면 안됨

 

 

 

3-2-1. @GeneratedValue 속성 

  • IDENTITY : 기본 키 생성을 DB에 위임
    • @GeneratedValue(strategy = GeneratedValue.IDENTITY)
    • mysql 이면 auto_increment (*아래 사진 참고)
@Entity
public class Member {
	@Id
	@GeneratedValue(strategy = GeneratedValue.IDENTITY)
	private String id;
    
	@Column(name="name", unique=false)
	private String username;
    
    ..
}

 

auto_increment

 

  • IDENTITY의 특징

영속성 컨텍스트에 값이 들어가려면 pk가 있어야하는데 IDENTITY 는 DB에 넣어봐야 pk 값을 알 수 있다. 

그래서 IDENTITY 전략에서만 em.persist()로 영속성 상태를 만드는 시점에 바로 DB에 INSERT SQL 쿼리를 실행하여 DB에 식별자를 조회한다. 

( JPA의 장점인 커밋 시점에 INSERT 쿼리를 모아서 DB에 날리지만 IDENTITY 전략에서는 예외)

 

 

 

  • SEQUENCE : SEQUENCE 객체는 DB가 관리. (주로 오라클 DB에서 씀) 
    • @GeneratedValue(strategy = GeneratedValue.SEQUENCE)
    • SEQUENCE 객체를 통해 값(1,2,...)을 가져와 세팅을 한다
@Entity
public class Member {
	@Id
	@GeneratedValue(strategy = GeneratedValue.SEQUENCE)
	private Long id; // Integer보다 Long 이 더 큼
    
	@Column(name="name", unique=false)
	private String username;
    
    ..
}

 

 

  • TABLE : 키 생성 전용 테이블을 만들어서 시퀀스 흉내내는 전략
    • 모든 DB에 적용가능한 장점이 있다. 
    • 테이블을 직접 사용하니 성능이 떨어지는 단점이 있다. 

 

  • AUTO : 기본값이며 DB방언(오라클, MySQL)에 맞춰서 IDENTITY , SEQUENCE , TABLE  셋 중 하나로 자동생성된다.
    • GenerationType.AUTO 생략해도 기본이 AUTO

 

 

반응형
LIST
엔티티 매핑 
  • 객체와 테이블 매핑 : @Entity, @Table
  • 필드와 컬럼 매핑 : @Column
  • 기본 키 매핑 : @Id
  • 연관관계 매핑 : @ManyToOne, @JoinColumn

 

 

 

1. 객체와 테이블 매핑 (@Entity, @Table)

 

1-1. @Entity

: @Entity 가 붙은 클래스는 JPA가 관리하는 객체이다. 붙지않으면 JPA와 관련없는 클래스

: 엔티티가 매핑된 것들을 자동으로 테이블을 만들어낸다. 

@Entity
public class Member {
	@Id
	private Long id;
	private String name;
	private int age;
}

테이블 자동생

 

 

  • 주의할 점

- 기본 생성자 필수 (파라미터가 없는 public, protected 생성자)

- 저장할 필드에 final 사용하면 됨

 

  • 속성 : name (잘 쓰진 않음)
@Entity(name = "Member")
public class Member {
}

 

 

 

1-2. @Table

: 엔티티와 매핑할 테이블 지정

 

  • 속성

name : 테이블명을 지정하고 싶을때 

@Entity
@Table(name = "MBR")
public class Member {
}

 

 

 

 

 

2. 필드와 컬럼 매핑 : @Column

@Entity
public class Member {
	@Id
	private Long id;
    
	@Column(name="USERNAME", unique=true, length=10)
	private String name;
    
	private Integer age; //DB에도 숫자타입
    
	//객체에서 enum 타입을 쓸때
	@Enumerated(EnumType.STRING)
	private RoleType roleType;
    
	@Temporal(TemporalType.TIMESTAMP)
	private Date lastModifiedDate;
    
	//varchar 보다 큰 크기
	@Lob
	private String description; // @Lob + 문자열이면 'clob' 이름으로 매핑이 됨
}

 

  • 속성
    • name="USERNAME" : DB의 데이터 컬럼명을 바꾸고싶을 때 
    • unique=true : 유니크 제약조건. DDL 생성, (DB에만 영향을 주고 JPA 실행 로직에는 영향 X)
      • @Column 안에서 잘 쓰지않고 클래스에서 @Table(uniqueConstraints="") 이 방식을 더 선호 
    • length=10 : 문자 길이 제약조건
    • nullable = true/false : null 값 허용 여부
    • insertable , updatable : 등록, 변경 가능여부 
      • ex) updatable=false 하면 업데이트를 해도 DB에 반영되지 않음
    • precision , sacle : 아주 큰 숫자나 소수점 쓸 때 

 

 

 

enum 타입
@Enumerated

객체에서 enum 타입을 쓴다면 DB에는 enum 타입이 없기때문에 @Enumerated 어노테이션을 쓴다.

public enum RoleType {
	USER, ADMIN
}
@Enumerated(EnumType.STRING)
private RoleType roleType;

 

  • 주의사항

기본이 ORDINAL 인데 사용하지 않는다 

> EnumType.ORDINAL : enum 순서를 DB에 저장 (숫자 타입으로 DB에 저장이 된다.  0,1,2,3...)

현재 enum 타입을 쓰는 객체 USER, ADMIN 은 각 0번, 1번 순서

 

쓰면 안되는 이유 

enum 타입을 쓰는 객체 GUEST를 추가했을때 GUEST도 0번째가 되어버려서 

USER도 0번 순서, GUEST도 0번 순서로 겹쳐버리기 때문에 필수로 EnumType.STRING 를 사용한다.

public enum RoleType {
	GUEST, USER, ADMIN
}

 

 

그렇기 때문에 문자타입인 EnumType.STRING을 쓴다면 GUEST, USER, ADMIN 구분이 쉽게 된다.

 

 

 

날짜 타입
@Temporal
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
  • DATE : 날짜
  • TIME : 시간
  • TIMESTAMP : 날짜,시간

 

* DB와 관련없이 메모리에만 객체를 쓰고싶다 > @Transient 

* 최신버전은 LocalDate createDate : 년월 / LocalDateTime : 년월시간

 

 

 

 

 

 

 

 

 

 

기본키 매핑은 다음 글...

반응형
LIST

h2 라이브러리 사용을 하는데 이클립스 실행하면 에러가 나서 

아래의 세가지 방법 중 해결해보면 됐음. 

 

1. 자바 환경변수 다시 설정

 

2. Connection is broken: "java.net.SocketTimeoutException: connect timed out

h2 DB와 연결되지 않아서 발생한 에러

h2.bat 실행하니까 이클립스 실행됨

 

3. NoClassDefFoundError

자바 환경변수 다시 확인하고 properties 에서 jdk1.8로 돼있는지 확인 (11로 돼있어서 안됐음)

 

 

 

반응형
LIST

+ Recent posts