โ SOLID ์์น์ด๋?
๊ฐ์ฒด์งํฅ ์ค๊ณ์์ ์ง์ผ์ผ ํ 5๊ฐ์ ์ํํธ์จ์ด ๊ฐ๋ฐ ์์น์ผ๋ก SRP, OCP, LSP, ISP, DIP์ ์๊ธ์๋ฅผ ๋ฐ์ ๋ง๋ค์ด์ก์ต๋๋ค.
์ ์ค๊ณ๋์๋์ง ํ๊ฐํ๋ ๋ฐ์ ๋์์ด ๋๋ ์ค๊ณ ์์น์ ๋๋ค.
- SRP: Single Responsibility Principle, ๋จ์ผ ์ฑ ์์ ์์น
- OCP: Open Closed Principle, ๊ฐ๋ฐฉ ํ์์ ์์น
- LSP: Liskov Substitution Principle, ๋ฆฌ์ค์ฝํ ์นํ์ ์์น
- ISP: Interface Segregation Principle, ์ธํฐํ์ด์ค ๋ถ๋ฆฌ์ ์์น
- DIP: Dependency Inversion Principle, ์์กด ์ญ์ ์ ์์น
๐ท ๋์ ์ค๊ณ์ ํน์ง
- ๊ฒฝ์ง์ฑ(Shotgun): ํ๋๋ฅผ ๊ณ ์น๋ฉด ๋ง์น ๋๋ฏธ๋ ธ์ฒ๋ผ ์ํฅ์ด ํผ์ ธ์ ์ ์ฒด์ ์ธ ์ฝ๋์ ๋ฌธ์ ๊ฐ ์๊ธด๋ค.
- ๋ถ์์ง๊ธฐ ์ฌ์: ํ๋๋ฅผ ๊ณ ์ณค๋๋ฐ ์๋์ด ๋ฉ์ถ๋ค.
- ๋ถ๋์ฑ: ์ฌ์ฌ์ฉ์ด ์ด๋ ต๋ค.
- ๋๋ํจ(long cycle): ํ๋๋ฅผ ๊ณ ์น๋ฉด ๋ค๋ฅธ ๊ณณ๋ ๊ณ์ ๊ณ ์ณ์ผ ํด์ ์์ฑ์ด ์๋๋ค.
- ์ธ๋ฐ์์ด ๋ณต์กํจ: ๋ถํ์ํ๊ฒ ์ฝ๋๊ฐ ๋ณต์กํ๋ค.
- ํ์ ์๋ ๋ฐ๋ณต: ์ค๋ณต๋ ์ฝ๋๊ฐ ๋ง๋ค.
- ๋ถํฌ๋ช ํจ: ์ฝ๋์ ์๋๊ฐ ํฌ๋ฏธํ๋ค.
๐ท ์คํ๊ฒํฐ ์ฝ๋
๋์ ์ค๊ณ์ ๋ํ์ ์ธ ์ฝ๋์ ๋๋ค.
์์ค ์ฝ๋๊ฐ ๋ณต์กํ๊ฒ ์ฝํ ๋ชจ์ต์ด ๋ง์น ์คํ๊ฒํฐ์ ๋ฉด๋ฐ๊ณผ ๊ฐ๋ค๋ ์๋ฏธ์์ ์คํ๊ฒํฐ ์ฝ๋๋ผ๊ณ ๋ถ๋ฆฝ๋๋ค.
๋ก์ง์ด ๊ต์ฅํ ๋ณต์กํ์ฌ ์์ ์ด ์ด๋ ต๋ค๋ ํน์ง์ด ์์ต๋๋ค.
1๏ธโฃ SRP(Single Resposibility Principle, ๋จ์ผ ์ฑ ์์ ์์น)
ํด๋์ค๋ ๋จ ํ๋์ ์ฑ ์๋ง ๊ฐ์ ธ์ผ ํฉ๋๋ค. ์ฆ, ์ด๋ค ํด๋์ค๋ฅผ ๋ณ๊ฒฝํด์ผ ํ๋ ์ด์ ๋ ์ค์ง ํ๋์ฌ์ผ ํฉ๋๋ค.
๊ฒฐํฉ๋ ์ฑ ์์ ๋ถ๋ฆฌํ์ฌ ๋ณ๋์ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
์ฌ๊ธฐ์ ์ฑ ์์ด๋ ์ฝ๊ฒ '๊ธฐ๋ฅ'์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค.
์์๋ฅผ ์ดํด๋ด ์๋ค.
public class Student{
// ๊ฐ ํ๋์ getter์ setter๊ฐ ์กด์ฌํ๋ค๊ณ ๊ฐ์
private int id;
private string Name;
private string Birth;
private char Gender;
public object Export(string format); // ์ฃผ์ด์ง ํฌ๋งท์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ํจ์
}
[๋ฌธ์ ์ ]
์ ์์์์ Student์ ์ฑ ์์ ํฌ๊ฒ 2๊ฐ์ง๋ก ๋๋ ์ ์์ต๋๋ค.
(1) ํ์์ ํ๋ฒ ์ด๋ฆ, ์๋ ์์ผ, ์ฑ๋ณ์ ์ ์ฅํ๋ ์ญํ
(2) ๋ค๋ฅธ ํฌ๋งท์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ์ญํ
=> ํ ํด๋์ค๊ฐ ์ฌ๋ฌ ์ฑ ์์ ๋งก๊ณ ์์ต๋๋ค.
[์์น ์ ์ฉ]
๋ฐ๋ผ์ Export ํจ์๋ ์๋ก์ด ํด๋์ค๋ฅผ ๋ง๋ค์ด ์ฑ
์์ ๋ถ๋ฆฌํด์ผ ํฉ๋๋ค.
SRP ์์น์ ์ ์ฉํ๋ฉด ์ฝ๋๋ ์๋์ ๊ฐ์ด ๋ณ๊ฒฝ๋ฉ๋๋ค.
public class Student{
// ๊ฐ ํ๋์ getter์ setter๊ฐ ์กด์ฌํ๋ค๊ณ ๊ฐ์
private int id;
private string Name;
private string Birth;
private char Gender;
}
public class Exporter{
public object Export(string format);
}
๐ท ์ฐํ์ด ์์ (Shotgun Surgery)
์์์๋ ํ๋์ ํด๋์ค๊ฐ ์ฌ๋ฌ ์ฑ ์์ ๊ฐ์ง๋ ๊ฒฝ์ฐ์ ๋ํด์ ๋ค๋ค์ต๋๋ค.
๊ทธ๋ฌ๋ฉด ์ฌ๋ฌ ํด๋์ค๊ฐ ํ๋์ ์ฑ ์์ ๋๋ ๊ฐ์ง๋ ๊ฒฝ์ฐ๋ ์ด๋จ๊น์? ์ฌ๊ธฐ์ ๋์จ ๊ฐ๋ ์ด ๋ฐ๋ก ์ฐํ์ด ์์ ์ ๋๋ค.
์ฐํ์ด์ ์ด์ ํ๋์ ์ฌ๋ฌ ๊ฐ์ ํ์ด ๋ค์ด์์ด ์ด์์ ๋ง๊ฒ ๋๋ฉด ์ฌ๋ฌ ๊ฐ์ ํ์ ์ผ์ผ์ด ์ฐพ์์ ์น๋ฃํด์ผ ํฉ๋๋ค.
์ฌ๊ธฐ์ '์ด์'์ '์ฑ ์'์ผ๋ก, 'ํ'์ 'ํด๋์ค'๋ก ๋น์ ํ ์ ์์ต๋๋ค.
์ฆ, ์ฐํ์ด ์์ ์ด๋ ํ๋์ ์ฑ ์์ด ์ฌ๋ฌ ํด๋์ค๋ค๋ก ๋ถ์ฐ๋์ด ์๋ ๊ฒฝ์ฐ๋ ๋จ์ผ ์ฑ ์์ ์์น์ ์ ์ฉํด ์ค๊ณ๋ฅผ ๋ณ๊ฒฝํด์ผ ํ๋ค๋ ์๋ฏธ์ ๋๋ค.
2๏ธโฃ OCP(Open Closed Principle, ๊ฐ๋ฐฉ ํ์์ ์์น)
์ํํธ์จ์ด์ ์์๋ ํ์ฅ์ ๋ํด์๋ ์ด๋ ค ์์ง๋ง, ๋ณ๊ฒฝ์ ๋ํด์๋ ๋ซํ ์์ด์ผ ํฉ๋๋ค.
์๊ตฌ์ฌํญ์ ๋ณ๊ฒฝ ๋ฐ ์ถ๊ฐ๊ฐ ์ผ์ด๋ฌ์ ๋, ๊ธฐ์กด ๊ตฌ์ฑ ์์๋ ์์ ์ด ์ผ์ด๋์ง ์์์ผ ํ๋ฉฐ ์ฝ๊ฒ ํ์ฅ์ด ๊ฐ๋ฅํด์ ์ฌ์ฌ์ฉํ ์ ์์ด์ผ ํฉ๋๋ค.
์์๋ฅผ ์ด์ด๊ฐ ๋ด ์๋ค.
public class Exporter{
public object Export(string format){
switch(format){
case "Grade": // ์ฑ์ ํ
... // ํ์์ ๋ชจ๋ ์ฑ์ ์ด ์์
ํ๋ก ์ ์ฅ
case "Attendance": // ์ถ์๋ถ
... // ์ด๋ฆ, ํ๋ฒ ์์์ ๋ฌธ์์ด ๋ฐํ
default:
...
}
}
}
[๋ฌธ์ ์ ]
Export ํจ์์ ๋ด๋ถ๋ฅผ ์ดํด๋ณด๋ฉด switch-case๋ฌธ์ ์ฌ์ฉํ์ฌ ๊ตฌํ๋์ด ์์ต๋๋ค.
๋ง์ฝ ์๋ก์ด format ํ์์ด ์ถ๊ฐ๋๋ค๋ฉด, case๋ฌธ์ ํ๋ ๋ ์ถ๊ฐํด์ผ ํ๋ฏ๋ก Exporter ํด๋์ค์์ ๋ณ๊ฒฝ์ด ํ์ํฉ๋๋ค.
=> ๊ธฐ์กด ๊ตฌ์ฑ ์์๋ฅผ ์์ ํ์ง ์๊ณ ์ฝ๊ฒ ํ์ฅ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค.
[์์น ์ ์ฉ]
OCP ์์น์ ์งํค๋ ค๋ฉด Exporter๋ฅผ ์ถ์ํํ๊ณ , ์ด๋ฅผ ์์๋ฐ๋ ํด๋์ค๋ฅผ format๋ณ๋ก ๋ง๋ค์ด์ผ ํฉ๋๋ค.
public abstract class Formatter{
public abstract Stream Format(Student student);
}
public class AttendanceFormatter extends Formatter{
public Stream Format(Student student){
return AttendanceStream;
}
}
public class GradeFormatter extends Formatter{
public Stream Format(Student student){
excel.save(location);
return GradeStream;
}
}
3๏ธโฃ LSP(Liskov Substitution Principle, ๋ฆฌ์ค์ฝํ ๊ต์ฒด์ ์์น)
์๋ธ ํ์ ์ ๋ฒ ์ด์ค ํ์ ์ ๊ต์ฒด๋ ์ ์์ด์ผ ํ๋ค๋ ์์น์ ๋๋ค.
์ํผ ํด๋์ค์์ ๋ฐ๋ ๋งค๊ฐ๋ณ์๋ ์๋ธ ํด๋์ค๊ฐ ๋ค ๋ฐ์์ผ ํ๋ฉฐ, ์ํผ ํด๋์ค๊ฐ ์ฌ์ฉ๋์์ ๋ ์ ํจํ๋ฉด ์๋ธ ํด๋์ค๊ฐ ์ฌ์ฉ๋์์ ๋๋ ์ ํจํด์ผ ํฉ๋๋ค.
OCP ์์น์ ์ ์ฉํ ํ์ ์ฝ๋๋ฅผ ๋ค์ ๋ด ์๋ค.
public abstract class Formatter{
public abstract Stream Format(Student student);
}
public class AttendanceFormatter extends Formatter{
public Stream Format(Student student){
return AttendanceStream;
}
}
public class GradeFormatter extends Formatter{
public Stream Format(Student student){
excel.save(location);
return GradeStream;
}
}
[๋ฌธ์ ์ ]
(1) GradeFormatter์ Format ํจ์๋ฅผ ๋ณด๋ฉด location์ด๋ผ๋ ๋ณ์๊ฐ ์ฐ์ด๊ณ ์์ต๋๋ค. ํ์ง๋ง ์ด ๋ณ์๋ ์ด๋์์๋ ๋ฐ์์ค์ง๋ ์ ์ธ๋์ด ์์ง๋ ์์ต๋๋ค. ์ฆ, ํ๋ผ๋ฏธํฐ๋ก location ๋ณ์๋ฅผ ๋ฐ์์์ผ ํฉ๋๋ค.
(2) ๋ํ, ์ง๊ธ Format ํจ์์ ๋ฐํ๊ฐ์ด Stream์ผ๋ก ์ ์ธ๋์ด ์๋๋ฐ, Attendance์ผ ๋๋ string์ด, Grade์ผ ๋๋ null์ด ๋ฐํ๋์ด์ผ ํฉ๋๋ค.
=> ๋ถ๋ชจ ํด๋์ค๋ฅผ ์๋ธ ํด๋์ค๋ค์ด ์์ ํ ๋์ฒดํ์ง ๋ชปํ๊ณ ์์ต๋๋ค.
[์์น ์ ์ฉ]
LSP ์์น์ ์ ์ฉํ์ฌ ๊ณ ์ณ๋ด ์๋ค.
public abstract class Formatter{
public abstract Object Format(Student student, string? location);
}
public class AttendanceFormatter extends Formatter{
public String Format(Student student, string? location){
return attString;
}
}
public class GradeFormatter extends Formatter{
public Object Format(Student student, string? location){
excel.save(location);
return null;
}
}
๋ถ๋ชจ ํด๋์ค์ (1) ํ๋ผ๋ฏธํฐ์ location์ ์ถ๊ฐํ๊ณ , (2) ๋ฐํ ํ์ ์ Object๋ก ๋ณํํ์ต๋๋ค.
์ฝ๋์์ `string?` ๋ฌผ์ํ์ ์๋ฏธ๋ null ๊ฐ์ผ ์๋ ์๊ณ ์๋ ์๋ ์๋ค๋ ์๋ฏธ์ ๋๋ค. ํฌ๋งท์ด Attendance์ผ ๋๋ location ๊ฐ์ด null์ด๊ณ Grade์ผ ๋๋ null์ด ์๋๋ฏ๋ก ์ด๋ฅผ ๋ฐ์ํ๊ธฐ ์ํจ์ ๋๋ค.
๋ฐํ ํ์ ์ Object๋ก ๋ณํํจ์ผ๋ก์จ ํฌ๋งท์ด Attendance ์ผ ๋๋ string์ ๋ฐํํ๊ณ Grade ์ผ ๋๋ null ๊ฐ์ ๋ฐํํ ์ ์๊ฒ ๋์์ต๋๋ค. ์ฐธ๊ณ ๋ก Object ํด๋์ค๋ ๋ชจ๋ ์๋ฐ ํด๋์ค์ ์ต๊ณ ์กฐ์ ํด๋์ค์ ๋๋ค.
4๏ธโฃ ISP(Interface Segregation Principle, ์ธํฐํ์ด์ค ๋ถ๋ฆฌ ์์น)
์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉ์ ๋ง๊ฒ ์ ๋ถ๋ฆฌํด์ผ ํ๋ค๋ ์์น์ ๋๋ค.
์ฆ, ํด๋ผ์ด์ธํธ๋ ์์ ์ด ์ฌ์ฉํ์ง๋ ์๋ ์ธํฐํ์ด์ค ๋ฉ์๋์ ์์กดํ๋ฉด ์ ๋ฉ๋๋ค.
SRP๊ฐ ํด๋์ค์ ๋จ์ผ ์ฑ ์์ ์ฃผ์ฅํ๋ค๋ฉด, ISP๋ ์ธํฐํ์ด์ค์ ๋จ์ผ ์ฑ ์์ ์ฃผ์ฅํ๋ค๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค.
์์ ์ด ์ฝ๋๋ฅผ ์ด์ง ์์ ํด์ ISP ์์น์ ๋ถํฉํ์ง ์๋ ์์๋ฅผ ์ดํด๋ด ์๋ค.
public interface Formatter{
public void Save(Stduent student);
public Stduent Get(int id);
}
public class ReadOnlyRepository implements Formatter{
public void Save(Stduent student){
throw NotImplementedException();
}
public Stduent Get(int id){
return db.findById(id);
}
}
[๋ฌธ์ ์ ]
๊ฐ๋จํ๊ฒ ๋ฉ์๋ 2๊ฐ๋ง ์๋ค๊ณ ๊ฐ์ ํด ๋ด ์๋ค.
ReadOnlyRepository ํด๋์ค๋ ์ด๋ฆ์์ ๋ณด๋ฏ์ด db๋ก๋ถํฐ ์ฝ๋ ์ญํ ๋ง ์ํํ๊ณ ์ถ์ด ํฉ๋๋ค.
๊ทธ๋์ Save ํจ์๋ฅผ NotImplementedException์ ํตํด '๋๋ ์ด ํจ์๋ฅผ ๊ตฌํํ์ง ์์ ๊ฑฐ๋ค~'๋ผ๊ณ ๋งํ๊ณ ์์ต๋๋ค.
=> ์ฌ์ฉํ์ง ์๋ ์ธํฐํ์ด์ค๋ฅผ ํ์ฌ ๊ฐ์ ๋ก ๊ตฌํํ๊ณ ์์ต๋๋ค.
[์์น ์ ์ฉ]
ISP ์์น์ ๋ง๊ฒ ๊ณ ์น๋ ค๋ฉด ๊ฒฐ๊ตญ readonly ์ฉ๋์ ์ธํฐํ์ด์ค์ writeonly ์ฉ๋์ ์ธํฐํ์ด์ค ๋ ๊ฐ๋ก ๋ถ๋ฆฌํด์ผ ํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ReadOnlyRepository๋ readonly ์ฉ๋์ ์ธํฐํ์ด์ค๋ง ๊ตฌํํ๋๋ก ์์ ํด์ผ ํฉ๋๋ค.
์์ ์ด ๊ฐ๋จํ๊ธฐ ๋๋ฌธ์ ์ฝ๋๋ ์๋ตํ๊ฒ ์ต๋๋ค.
5๏ธโฃ DIP(Dependency Inversion Principle, ์์กด ์ญ์ ์ ์์น)
๊ตฌํ ํด๋์ค์ ์์กดํ์ง ๋ง๊ณ , ๊ทธ ๋์์ ์์ ์์์ธ ์ถ์ ํด๋์ค๋ ์ธํฐํ์ด์ค์ ์์กดํด์ผ ํ๋ค๋ ์์น์ ๋๋ค.
์ฆ, ๊ตฌ์ฒด์ ์ธ ๊ฒ์ด ์๋ ์ถ์์ ์ธ ๊ฒ์ ์์กดํด์ผ ํ๋ค.
์ด๋ ์ถ์์ ์ธ ๊ฐ๋ ๋ณด๋ค ๊ตฌ์ฒด์ ์ธ ๊ฐ๋ ์ด ๋ ๋ง์ด ๋ณํ๊ณ ๋ถ์์ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ถ์์ ์ธ ๊ฐ๋ ์ ๋ณ๊ฒฝ์ด ๋์ง ์๋ ๋ช ์ธ์ ์ธ ๋ถ๋ถ์ ๋๋ค. ํด๋ผ์ด์ธํธ ์ ์ฅ์์๋ ์ถ์์ ์ธ ๊ฒ, ๋ณํ์ง ์๋ ๋ถ๋ถ์ ์์กดํด์ผ ํฉ๋๋ค.
์๋ก์ด ์์๋ฅผ ์ดํด๋ด ์๋ค.
public class Kid {
private Robot toy;
public void setToy(Robot toy) {
this.toy = toy;
}
public void play() {
System.out.println(toy.toString());
}
}
public class Main{
public static void main(String[] args) {
Robot robot = new Robot();
Kid k = new Kid();
k.setToy(robot);
k.play();
}
}
[๋ฌธ์ ์ ]
์ ์ฝ๋์์ Kid๋ Robot์ ์์กดํ๊ณ ์์ต๋๋ค.
๋ง์ฝ toy๋ก Robot์ด ์๋ Lego๋ฅผ ํ ๋นํ๊ณ ์ถ์ผ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
Kid ๋ด๋ถ์ ๋ชจ๋ Robot ๋ถ๋ถ ์ฝ๋๋ฅผ Lego๋ก ์์ ํด์ผ ํฉ๋๋ค. ์๋๋ฉด ์๋ก์ด Lego toy ๊ฐ์ฒด์ ์์กดํ๋๋ก ์ถ๊ฐํด์ผ ํฉ๋๋ค.
=> ๊ตฌํ ํด๋์ค์ ์์กดํ๊ณ ์์ต๋๋ค.
[์์น ์ ์ฉ]
์ฌ๊ธฐ์ DIP ์์น์ ์ ์ฉํ๋ฉด, Robot๊ณผ Lego ํด๋์ค์ ์์ ์์์ธ Toy ์ธํฐํ์ด์ค๋ฅผ ๋ง๋ค๋ฉด ๋ฉ๋๋ค.
public class Kid {
private Toy toy;
public void setToy(Toy toy) {
this.toy = toy;
}
public void play() {
System.out.println(toy.toString());
}
๋ง์ฝ ํด๋ผ์ด์ธํธ๊ฐ toy๋ก ์๋ก์ด ์ฅ๋๊ฐ์ ํ ๋นํ๊ณ ์ถ๋ค๋ฉด, Toy์ ํ์ ํด๋์ค๋ก ์๋ก์ด ์ฅ๋๊ฐ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ฉด ๋์ ๋๋ค.
Kid์ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ ํ์ ์์ต๋๋ค.
'๐ฏ Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JAVA] ์ค๋ณต ์ ๊ฑฐ ๋ฐฉ๋ฒ(+์ ๋ ฌ) (1) | 2025.01.31 |
---|---|
[Github Action] build ์ค๋ฅ ์์ธ ๋ ์์ธํ ๋ณด๋ ๋ฒ (0) | 2024.10.08 |
[ISSUE] boolean์์ is๊ฐ ์๋ต๋๋ ๋ฌธ์ (0) | 2024.09.06 |
์ธํฐํ์ด์ค(Interface) vs ์ถ์ ํด๋์ค(Abstract class) (0) | 2024.05.25 |