I've been using Spring Data JPA, but Kim Young-han actually teaches JPA itself.
Based on his lectures, I decided to write this post to test the course material with Spring Data JPA and document the concepts.
Various Association Mappings
1. Three Things to Consider When Mapping Associations
1-1. Multiplicity
Many-to-One [N:1] : @ManyToOne
One-to-Many [1:N] : @OneToMany
One-to-One [1:1] : @OneToOne
Many-to-Many [N:M] : @ManyToMany
1-2. Unidirectional vs. Bidirectional
1-3. Owner of the Association
Tables establish an association between two tables with a single foreign key
In a bidirectional object relationship, there are two references: A→B and B→A
Since there are two references in a bidirectional object relationship, you need to designate which side manages the foreign key When you change A, should B also change? / When you change B, should A also change?
Owner of the association: the reference that manages the foreign key
Inverse side: does not affect the foreign key
2. Many-to-One [N:1]
Owner of the association: N side
2-1. Many-to-One Unidirectional
Member : N - Team : 1
Member references Team.
@Entity
@Getter
@Setter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID") // 외래키
private Team team;
}
@Entity
@Getter
@Setter
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
private String name;
}
You place the reference and map the association where the foreign key exists.
From the DB's perspective, the FK naturally belongs on the N side.
If it were on the Team side instead, you'd need a list, and the design wouldn't make sense.
This is the most commonly used mapping
The inverse of Many-to-One is One-to-Many
2-2. Many-to-One Bidirectional
Member : N - Team : 1
Member references Team. And Team also references Member!
The owner of the association manages the FK. Since the inverse side can only read anyway, you just need to add a List to Team.
Here, it's important to specify with mappedBy that this side reads from the association owner
// Team 클래스
@OneToMany(mappedBy = "team") //참조를 당하는 쪽에서 읽기만 가능!
private List<Member> members = new ArrayList<>();
The side with the foreign key is the owner of the association
Develop so that both sides reference each other
3. One-to-Many [1:N]
3-1. One-to-Many Unidirectional
This is not recommended.
Centered around Team: Team manages the foreign key
Team wants to know about Member, but Member doesn't want to know about Team.
From the DB's perspective: the FK must be on the Member table.
When you modify Team's List, you have to update the TEAM_ID of some row in the Member table in the DB.
@Entity
@Getter
@Setter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
}
@Entity
@Getter
@Setter
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<>();
}
The data goes into the DB fine, but extra queries like UPDATE queries are fired.
When saving the Member list from Team, the team_id in the Member table also needs to be updated.
You touched Team, but it affects the Member table — this makes it a bit harder to understand and trace.
In One-to-Many unidirectional, the One (1) side is the owner of the association
In a One-to-Many table relationship, the foreign key is always on the Many (N) side
Due to the mismatch between objects and tables, this results in an unusual structure where you manage the foreign key of the opposite table
You must use @JoinColumn. Otherwise, it uses a join table strategy (adds an intermediate table) — a middle table like team_member gets created with team_id and member_id. The downside: having one extra table makes operations harder.
Downsides of One-to-Many unidirectional mapping
The foreign key managed by the entity is in a different table
Additional UPDATE SQL is executed to manage the association
Use Many-to-One bidirectional mapping instead of One-to-Many unidirectional — sacrifice a bit on the object relationship side!
3-2. One-to-Many Bidirectional
This kinda works as a hacky workaround lol
// Member 클래스
@ManyToOne
@JoinColumn(name="TEAM_ID", insertable = false, updatable = false) //중요!!
private Team team;
But this way, both Team and Member have @JoinColumn, making both of them owners of the association.
So you use JoinColumn options — the mapping exists and values are all there, but you block insertable and updatable to make it read-only.
Management is done through Team, and Member only reads.
This mapping doesn't officially exist
@JoinColumn(insertable=false, updatable=false)
A way to use it like bidirectional by using a read-only field
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class MemberTest {
@Autowired
MemberRepository memberRepository;
@Autowired
TeamRepository teamRepository;
@Before
public void setUp() throws Exception {
Team team = Team.builder()
.name("TeamA")
.build();
// teamRepository.save(team);
Member member = Member.builder()
.username("member1")
.team(team)
.build();
memberRepository.save(member);
}
@Test
public void 잘_저장되었는지_불러오기() {
Member member = memberRepository.findAll().get(0);
String username = member.getUsername();
assertThat(username).isEqualTo("member1");
Team team = member.getTeam();
assertThat(team.getName()).isEqualTo("TeamA");
List<Member> members = team.getMembers();
for (Member m : members) {
assertThat(m.getUsername()).startsWith("member");
}
}
강좌랑 cascade 부분만 달라서 왜 그런가 하고 생각해 봤는데 강좌에서는 save를 두번 했었다. 강좌코드 대로 코딩하고 테스트한 결과는 아래
@Entity
@Getter
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
// @ManyToOne(cascade = CascadeType.ALL)
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
@Builder
private Member(String username, Team team) {
this.username = username;
this.team = team;
}
}
@Entity
@Getter
@NoArgsConstructor
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
@Builder
private Team(String name) { //여기 그냥 members도 param으로 넣었다가 에러 팡!
this.name = name;
}
}
테스트코드
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class MemberTest {
@Autowired
MemberRepository memberRepository;
@Autowired
TeamRepository teamRepository;
@Before
public void setUp() throws Exception {
Team team = Team.builder()
.name("TeamA")
.build();
teamRepository.save(team);
Member member = Member.builder()
.username("member1")
.team(team)
.build();
memberRepository.save(member);
}
@Test
public void 잘_저장되었는지_불러오기() {
Member member = memberRepository.findAll().get(0);
String username = member.getUsername();
assertThat(username).isEqualTo("member1");
Team team = member.getTeam();
assertThat(team.getName()).isEqualTo("TeamA");
List<Member> members = team.getMembers();
for (Member m : members) {
assertThat(m.getUsername()).startsWith("member");
}
}
}
Member 저장할 때 Team을 저장하도록 cascade 설정을 하지 않고,
TestCode 작성시에 Member save , Team save를 각각 해줬다
Team이 이미 save가 된 상태에서 Member를 save할 경우인데,
어차피 DB에서는 Member에 FK가 있기 때문에 매핑이 가능해진다!
객체에서의 매핑은 이미 Team, Member 모두 각자의 참조객체를 갖고있기 때문에 가능하고!
내가 처음에 작성한 코드의 경우에는 member만 저장해서 team도 같이 저장하는 것이었기 때문에 Member를 저장할 때 cascade 옵션을 줘야했다.
따라서 Member repository에 저장하더라도, Team의 Insert를 먼저 실행 후에, Member insert를 진행하여 Member Table의 FK에 Team을 저장해준다.
Hibernate: insert into team (member_id, name) values (null, ?)
Hibernate: insert into member (member_id, team_id, username) values (null, ?, ?)
Hibernate: select member0_.member_id as member_i1_0_, member0_.team_id as team_id3_0_, member0_.username as username2_0_ from member member0_
Q. 양방향 매핑이 좋은가?
A. 객체는 사실 단방향이 좋다! -> 신경쓸게 많음
2-3. 객체와 테이블이 관계를 맺는 차이
2-3-1. 객체의 연관관계 - 2개
Member -> Team 연관관계 1개 (단방향) - Team 레퍼런스 객체
Team -> Member 연관관계 1개 (단방향) - Member 레퍼런스 객체
객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개다
객체를 양방향으로 참조하려면 단방향 연관 관계를 2개 만들어야 한다.
class Member{
Team team; // TEAM -> Member (team.getMember())
}
class Team{
Member member; // MEMBER -> TEAM (member.getTeam())
}
2-3-2. 테이블의 연관관계 - 1개
Team <-> Member 연관관계 1개 (양방향) - FK하나로 양쪽의 연관관계 알 수 있음 (join)
테이블은 외래 키 하나로 두 테이블의 연관관계를 관리
MEMBER.TEAM_ID 외래 키 하나로 양방향 연관관계 가짐 (양쪽으로 조인할 수 있다.)
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID
2-4. 연관관계의 주인
딜레마가 생긴다 > solution : 둘중 하나로 외래키를 관리한다!
Team에 있는 List 로 FK를 관리할지
Member에 있는 Team으로 FK를 관리할지
2-4-1. 양방향 매핑 규칙
객체의 두 관계중 하나를 연관관계의 주인으로 지정
연관관계의 주인만이 외래 키를 관리 (등록, 수정)
주인이 아닌쪽은 읽기만 가능
주인은 mappedBy 속성 사용X
주인이 아니면 mappedBy 속성으로 주인 지정
mappedBy : 나는 누군가에 의해서 매핑이 되었어! 나는 주인이 아니야!
public class Team {
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}
public class Member {
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
mappedBy : 나는 team에 의해서 관리가 된다 : Member 객체의 team 변수에 의해서 관리된다.
@JoinColumn의 Team: 나는 앞으로 Team을 관리할꺼야
2-4-2. 누구를 주인으로?
외래키가 있는 곳을 주인으로 정해라
여기서는 Member.team이 연관관계의 주인!
성능 이슈!
Member의 경우에는 insert 쿼리 하나인데
Team의 경우에는 insert 쿼리 + update 쿼리
DB 입장에서 외래키가 있는 곳이 무조건 N
= N 이 있는 곳이 무조건 주인
= @ManyToOne 이 무조건 주인
3. 양방향 연관관계와 연관관계의 주인 : 주의점, 정리
3-1. 양방향 매핑시 가장 많이 하는 실수
연관관계의 주인에 값을 입력하지 않음
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class FailTest {
@Autowired
MemberRepository memberRepository;
@Autowired
TeamRepository teamRepository;
@Autowired
EntityManager entityManager;
@Test
public void 일차캐싱에_따른_저장_테스트() {
Team team = Team.builder()
.name("TeamA")
.build();
teamRepository.save(team);
Member member = Member.builder()
.username("member1")
.team(team)
.build();
// team.getMembers().add(member);
memberRepository.save(member);
// 주인(Member)이 연관관계를 설정하지 않음!!
// 역방향(주인이 아닌 방향)만 연관관계 설정
// entityManager.clear();
Team findTeam = teamRepository.findAll().get(0);
List<Member> members = findTeam.getMembers();
assertThat(members).isEmpty();
}
}
entityManager.clear(); 을 안했을 경우 : 1차 캐시를 해서 영속성 컨텍스트가 되어있는 상태 값 세팅 연관관계가 되어있는걸 그냥 가져온다. 이렇게 실행하면 DB에서 select 쿼리가 안 나간다.
Team이 그냥 영속성 컨텍스트에 들어가있어서, team에는 현재 member가 없는상태. 그러다보니 1차 캐싱으로 인해 아무것도 안들어가 있음!
Team team = Team.builder()
.name("TeamA")
.build();
teamRepository.save(team);
Member member = Member.builder()
.username("member1")
.team(team)
.build();
team.getMembers().add(member);
이런식으로 Member에 한줄을 넣어주기 보다! 연관관계 편의 메소드를 생성하자
Member에서 team을 set 해줄때 설정해버린다. - 하나면 세팅해도 두개가 같이 세팅이 되게!
I've been using Spring Data JPA, but Kim Young-han actually teaches JPA itself.
Based on his lectures, I decided to write this post to test the course material using Spring Data JPA and document the concepts.
Association Mapping Basics
1. Unidirectional Association
<Haven't watched this part yet>
2. Bidirectional Association and the Owner of the Relationship: Basics
Bidirectional association -> references go both ways.
Object: uses references Table: uses FK joins
We need to look at the paradigm difference between objects and tables.
2-1. Table Associations
There's no difference between unidirectional and bidirectional.
Whether you want to know TEAM->MEMBER or MEMBER->TEAM, you can find out by joining with the Foreign Key. Regardless of whether it's bidirectional or unidirectional, you can figure out all associations with just the FK.
2-2. Object Associations
If Member has a Team variable, it can navigate to Team. Team needs to have a List to navigate to Member.
Objects need to hold references to each other as member variables to be able to access one another.
[Note]: When using a List member variable, always initialize it with new ArrayList<>()! This prevents NullPointerError when calling add()!
@Entity
@Getter
@NoArgsConstructor
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
@Builder
private Team(String name) { //여기 그냥 members도 param으로 넣었다가 에러 팡!
this.name = name;
}
}
@Entity
@Getter
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name= "TEAM_ID")
private Team team;
@Builder
private Member(String username, Team team) {
this.username = username;
this.team = team;
}
}
Now graph traversal is possible in the reverse direction as well.
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class MemberTest {
@Autowired
MemberRepository memberRepository;
@Autowired
TeamRepository teamRepository;
@Before
public void setUp() throws Exception {
Team team = Team.builder()
.name("TeamA")
.build();
// teamRepository.save(team);
Member member = Member.builder()
.username("member1")
.team(team)
.build();
memberRepository.save(member);
}
@Test
public void 잘_저장되었는지_불러오기() {
Member member = memberRepository.findAll().get(0);
String username = member.getUsername();
assertThat(username).isEqualTo("member1");
Team team = member.getTeam();
assertThat(team.getName()).isEqualTo("TeamA");
List<Member> members = team.getMembers();
for (Member m : members) {
assertThat(m.getUsername()).startsWith("member");
}
}
The only difference from the course was the cascade part, and when I thought about why, it's because the course called save twice. Here's the test result after coding it exactly like the course:
@Entity
@Getter
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
// @ManyToOne(cascade = CascadeType.ALL)
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
@Builder
private Member(String username, Team team) {
this.username = username;
this.team = team;
}
}
@Entity
@Getter
@NoArgsConstructor
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
@Builder
private Team(String name) { //여기 그냥 members도 param으로 넣었다가 에러 팡!
this.name = name;
}
}
Test code
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class MemberTest {
@Autowired
MemberRepository memberRepository;
@Autowired
TeamRepository teamRepository;
@Before
public void setUp() throws Exception {
Team team = Team.builder()
.name("TeamA")
.build();
teamRepository.save(team);
Member member = Member.builder()
.username("member1")
.team(team)
.build();
memberRepository.save(member);
}
@Test
public void 잘_저장되었는지_불러오기() {
Member member = memberRepository.findAll().get(0);
String username = member.getUsername();
assertThat(username).isEqualTo("member1");
Team team = member.getTeam();
assertThat(team.getName()).isEqualTo("TeamA");
List<Member> members = team.getMembers();
for (Member m : members) {
assertThat(m.getUsername()).startsWith("member");
}
}
}
Without setting cascade on Member to also save Team,
I called save separately for both Member and Team in the test code.
Since Team is already saved before saving Member,
the mapping works because the FK exists in the Member table in the DB!
On the object side, the mapping works because both Team and Member already hold their own reference objects!
In my original code, I was saving only member and having team saved along with it, so I needed the cascade option on Member.
Therefore, even though we're saving through the Member repository, it executes the Team's INSERT first, then proceeds with the Member INSERT, storing the Team in the Member table's FK.
Hibernate: insert into team (member_id, name) values (null, ?)
Hibernate: insert into member (member_id, team_id, username) values (null, ?, ?)
Hibernate: select member0_.member_id as member_i1_0_, member0_.team_id as team_id3_0_, member0_.username as username2_0_ from member member0_
Q. Is bidirectional mapping better?
A. Actually, unidirectional is better for objects! -> There's a lot more to worry about
2-3. Differences in How Objects and Tables Form Relationships
2-3-1. Object Associations - 2
Member -> Team: 1 association (unidirectional) - Team reference object
Team -> Member: 1 association (unidirectional) - Member reference object
A bidirectional relationship in objects is not truly bidirectional — it's actually two separate unidirectional relationships
To reference objects bidirectionally, you need to create two unidirectional associations.
class Member{
Team team; // TEAM -> Member (team.getMember())
}
class Team{
Member member; // MEMBER -> TEAM (member.getTeam())
}
2-3-2. Table Associations - 1
Team <-> Member: 1 association (bidirectional) - A single FK lets you know both sides of the relationship (join)
Tables manage the association between two tables with a single foreign key
The MEMBER.TEAM_ID foreign key alone provides a bidirectional association (you can join from either side.)
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID
2-4. The Owner of the Relationship
A dilemma arises > solution: manage the foreign key from one side!
Should we manage the FK through the List in Team?
Or through the Team in Member?
2-4-1. Bidirectional Mapping Rules
Designate one of the two object relationships as the owner of the relationship
Only the owner of the relationship can manage the foreign key (create, update)
The non-owner side can only read
The owner does NOT use the mappedBy attribute
The non-owner uses mappedBy to specify the owner
mappedBy: "I'm mapped by someone else! I'm not the owner!"
public class Team {
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}
public class Member {
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
mappedBy: "I'm managed by team" — meaning it's managed by the team variable in the Member object.
@JoinColumn's Team: "I'm going to manage Team from now on"
2-4-2. Who Should Be the Owner?
Make the side where the foreign key exists the owner
In this case, Member.team is the owner of the relationship!
Performance issue!
For Member, it's just one INSERT query,
but for Team, it's an INSERT query + an UPDATE query
From the DB's perspective, the side with the foreign key is always the N (many) side
= The N side is always the owner
= @ManyToOne is always the owner
3. Bidirectional Association and the Owner of the Relationship: Pitfalls and Summary
3-1. The Most Common Mistake in Bidirectional Mapping
Not setting a value on the owner of the relationship
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class FailTest {
@Autowired
MemberRepository memberRepository;
@Autowired
TeamRepository teamRepository;
@Autowired
EntityManager entityManager;
@Test
public void 일차캐싱에_따른_저장_테스트() {
Team team = Team.builder()
.name("TeamA")
.build();
teamRepository.save(team);
Member member = Member.builder()
.username("member1")
.team(team)
.build();
// team.getMembers().add(member);
memberRepository.save(member);
// 주인(Member)이 연관관계를 설정하지 않음!!
// 역방향(주인이 아닌 방향)만 연관관계 설정
// entityManager.clear();
Team findTeam = teamRepository.findAll().get(0);
List<Member> members = findTeam.getMembers();
assertThat(members).isEmpty();
}
}
When entityManager.clear() is not called: Since first-level caching is active and the persistence context still holds the values, it just fetches the existing association state. When executed this way, no SELECT query is actually sent to the DB.
Team is just sitting in the persistence context, and at this point, it has no members. Because of first-level caching, nothing is populated!
From an object-oriented perspective, you should set values on both sides!
3-2. Bidirectional Association Precautions
Always set values on both sides, considering the pure object state
Create convenience methods for managing associations
Watch out for infinite loops in bidirectional mapping e.g.) toString(), Lombok, JSON serialization libraries
Team team = Team.builder()
.name("TeamA")
.build();
teamRepository.save(team);
Member member = Member.builder()
.username("member1")
.team(team)
.build();
team.getMembers().add(member);
Rather than adding a separate line for Member like this, create a convenience method for the association!
Set it up when setting the team on Member — so that setting one side automatically sets both!
At a mini seminar held by DSC Ewha, I explained the concept of request-response architecture and servers to members who were just getting started with development.
I also gave a brief overview of the development frameworks and stacks that I'm familiar with.
I gave three presentations at the mini seminar held by DSC Ewha. In the second session, I covered the basic concepts of git and github, along with the most essential commands :)
package com.jyami.baekjoon;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main_9095 {
public static void main(String args[]) {
Scanner s = new Scanner(System.in);
int num = s.nextInt();
int max = 0;
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
int input = s.nextInt();
list.add(input);
if (input > max)
max = input;
}
int dp[] = new int[max + 1];
dp[1] = 1;
dp[2] = 2;
dp[3] = 4;
for (int i = 4; i <= max; i++) {
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
}
for (Integer integer : list) {
System.out.println(dp[integer]);
}
}
}
As soon as I saw the problem, it looked similar to the 0-1 knapsack problem, so I figured it must be a DP problem and started scribbling like that to find the pattern lol
At first, when I was just looking at the sums of those numbers, it felt like something was repeating with additions going on, but I couldn't quite figure it out. Then when I looked at the total sum of everything, the recurrence relation became clear.
As expected, with DP problems, once you find the pattern, the code ends up being pretty short.
But honestly, no matter how much I thought about it, I couldn't figure out why this pattern occurs. I guess DP problems still feel tough since I haven't solved that many yet. So I asked the people in my study group! And it was solved right away!!
First, the cases where N is 1, 2, or 3 are given as base values. If we let A(n) be the number of ways to represent the variable n:
[Base Values]
A(1) = 1 A(2) = 2 A(3) = 4
[When N=4]
Number of ways to represent 1 + 3 [=A(3)] Number of ways to represent 2 + 2 [=A(2)] Number of ways to represent 3 + 1 [=A(1)]
So A(4) = A(3) + A(2) + A(1) = 4 + 2 + 1 = 7.
Looking at each case in detail:
1 + A(3)
2 + A(2)
3 + A(1)
1 + 3 1 + 1 + 2 1 + 2 + 1 1 + 1+ 1+ 1
2 + 2 2 + 1 + 1
3 + 1
Similarly,
[When N=5]
Number of ways to represent 1 + 4 [=A(4)] Number of ways to represent 2 + 3 [=A(3)] Number of ways to represent 3 + 2 [=A(2)]
Since adding 1, 2, or 3 to a previously formed number gives us the current number, we just need to add up the number of previous combinations that result in the current number when adding 1, when adding 2, and when adding 3.
If this problem were "Adding 1, 2, 3, 4" instead, to find A(n), we would need: 1 + A(n-1) 2+ A(n-2) 3 + A(n-3) 4 + A(n-4) — these would be the cases that produce A(n).
By the same logic, if this problem were "Adding 1, 2", to find A(n), we would need: 1 + A(n-1) 2+ A(n-2) — these would be the cases that produce A(n), right?
package com.jyami.baekjoon;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main_9095 {
public static void main(String args[]) {
Scanner s = new Scanner(System.in);
int num = s.nextInt();
int max = 0;
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
int input = s.nextInt();
list.add(input);
if (input > max)
max = input;
}
int dp[] = new int[max + 1];
dp[1] = 1;
dp[2] = 2;
dp[3] = 4;
for (int i = 4; i <= max; i++) {
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
}
for (Integer integer : list) {
System.out.println(dp[integer]);
}
}
}
1. SW 객체의 자율성 (authonomous) : 상태와 행위를 캡슐화(encapsulation)
2. 객체는 협력한다 (collaboration) : 객체끼리 협력이 가능한 수단은 메세지(message)
2. 연결 완전성 (seamlessness) : 실세계 사물을 기반으로 SW 객체를 식별한 후 구현까지 이어나간다.
상태와 행위
상태는 JAVA의 member 변수 행위는 method를 의미 할 것 같다.
1. 협력하는 사람들
협력에 참여하는 모든 사람이 역할에 따른 책임을 완수해야한다.
협력에는 여러 객체들이 참여한다.
책임 - 객체가 가지는 모든 메서드
역할 - 협력 내에서 쓰이는 책임이 역할
협력 (collaboration) = 요청 (request) +응답 (response)
요청이 요청을 연쇄한다 요청 -> 요청 -> 요청
요청과 응답은 서로 반대 방향으로 이루어진다.
역할이 있으면 책임도 주어진다.
1-1. 협력의 특징
여러사람이 동일 역할을 수행 가능하다
역할 = 대체가능성 (substitude)
책임을 수행하는 방법은 자율적으로 선택할 수 있다. = 동일 요청에 대해 서로 다른 방식으로 응답이 가능하다 (다형성 : polymorphism)
한 사람이 동시에 여러 역할을 수행할 수 있다.
2. 역할, 책임, 협력
사람 -> 객체 에이전트의 요청 -> 메시지
에이전트의 요청 처리법 -> 메서드
협력 = 특정 책임을 수행하는 역할들 간의 연쇄적인 요청과 응답으로 목표를 달성하는 것
목표 = 어플리케이션의 기능을 구현하는 것
목표를 책임으로 작게 분할하여 -> 역할을 지닌 객체가 적절하게 처리가능하게 한다.
적절한 역할을 가진 객체가 처리하게 한다.
기능 = 객체들간의 요청 응답을 주고받는 것
2-1. 객체의 역할
여러 객체가 동일 역할을 수행
역할 = 대체 가능성
각 객체는 책임 수행할 수 있는 방법을 자율적으로 수행 가능하다
하나의 객체가 동시에 여러 역할을 수행 가능하다
3. 협력속에 사는 객체
패러다임의 중심 = 객체
3-1. 객체란?
협력에 참여하는 주체
애플리케이션 기능 구현을 위해 존재 : 혼자서 어려우니 다른 객체와 협력
객체지향의 아름다움 = 협력
협력의 조화 = 객체의 품질이 좋다
[3단 논법에 의해!!]
객체지향의 아름다움 = 객체의 품질이 좋다는 것
3-2. 객체의 두가지 덕목
객체는 '협력적'이어야 한다.
다른 객체에서 오는 요청 응답을 받는다 god object: 도움을 무시하고 스스로 모든 것을 처리하는 객체 (안좋다)
응답 방식 : 객체가 결정
응답 여부 : 객체가 결정한다.
객체는 '자율적'이어야 한다.
자신의 행동을 스스로 결정하고 책임진다.
요청에 대해 스스로 판단하고 행동한다
ex ) 손님이 캐시어에게, 바리스타한테 어떤 방식으로 전달하라 명령하지 않음
3-3. 객체란
= 상태(state)와 행동(behavior)을 함께 지닌 실체
= 내부와 외부의 명확한 구분이 있다.
내부 : 사적인 부분 스스로 관리 외부 : 접근 허가된 수단으로만 의사소통 ( what O / How X )
= 프로세스의 틀 안에서 자율성을 보장한다
자율성 보장 > 유지보수 용이 > 재사용 용이한 시스템 구축
상태와 행동은 자율성의 핵심 이유인 듯
- 행동을 하려면 이 행동을 하는데 필요한 상태가 있어야 하므로
내부 : 자율성을 의미하는 것 같다. (private 보장) 외부 : 협력적을 의미하는 것 같다. (public하게 협력)
3-4. 메세지(message)
= 객체들의 유일한 의사소통 수단
3-5. 메서드(method)
= 객체가 수신된 메세지를 처리하는 방법
메세지를 전송하면 메시지에 대응되는 메서드를 실행한다
처음에는 메세지가 내가 아는 메서드의 parameter이고 method를 이용해 행동을 처리해서 그 결과가 나오는 것이라고 메세지와 메서드를 이해했다. 아래와 같이!
message를 이용해서 요청이 들어오면 그 요청값을 알맞는 method가 받아서 실행한다고 생각했는데
public void method ( T message ){
..method의 수행내용
}
스터디원들하고 토론하다보니 메서드의 이름이 메세지라는 결론을 내렸다.
public void message ( ){
message에 알맞는 행동을 한다 : method
}
우리가 코드를 짤 때, 자바 method의 네이밍을 우리가 알기쉽게 직관적으로 (예를들면 getName())과 같이 명령을 하니 method의 이름 자체가 message라고 판단을 하게 되었다. 또한 해당 method는 들어온 param에 따라 message가 전달한 수행하라는 행동을 자율적으로 결정한다.
4. 객체 지향의 본질
객체 지향 : 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분별하는 방법
자율적인 객체 : 상태와 행위를 함께 지니며 스스로 자기 자신을 책임지는 객체
객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 책임의 집합이다
객체는 다른 객체와 협력하기 위해 메시지를 전송하고 메시지를 수신한 객체는 메시지를 처리하는데 적합한 메서드를 자율적으로 선택한다
댓글
Comments