객체지향 프로그래밍(OOP)의 네 가지 핵심 원칙인 상속, 다형성, 캡슐화, 추상화는 효율적이고 유지보수 가능한 코드를 작성하는 데 필수적인 개념입니다.
이 글에서는 각 원칙의 실전 활용 방법을 살펴보겠습니다.
상속(Inheritance)의 실전 활용
상속은 기존 클래스의 속성과 메서드를 새 클래스가 물려받는 메커니즘입니다. 이를 통해 코드 재사용성을 높이고 계층적 관계를 구현할 수 있습니다.
실제 예제: 차량 관리 시스템
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
// 부모 클래스
class Vehicle {
protected String modelName;
protected String brand;
public Vehicle(String modelName, String brand) {
this.modelName = modelName;
this.brand = brand;
}
public void startEngine() {
System.out.println(“Engine started for “ + modelName + “!”);
}
public void stopEngine() {
System.out.println(“Engine stopped for “ + modelName + “.”);
}
}
// 자식 클래스
class Car extends Vehicle {
private int numberOfDoors;
public Car(String modelName, String brand, int numberOfDoors) {
super(modelName, brand);
this.numberOfDoors = numberOfDoors;
}
public int getNumberOfDoors() {
return numberOfDoors;
}
// 메서드 오버라이딩
@Override
public void startEngine() {
System.out.println(“Car engine started for “ + modelName + “!”);
}
}
|
cs |
상속 활용 모범 사례
활용 방법 | 설명 |
---|---|
코드 재사용 | 공통 기능을 부모 클래스에 구현하여 중복 코드 제거 |
메서드 오버라이딩 | 부모 클래스의 메서드를 자식 클래스에서 재정의하여 특화된 동작 구현 |
super 키워드 활용 | 부모 클래스의 메서드를 호출하여 기능 확장 |
protected 접근 제어자 | 자식 클래스에서만 접근 가능한 멤버 정의 |
상속의 주의점
상속보다 합성(Composition)을 선호하는 것이 좋습니다. 상속은 강한 결합도를 만들어 유연성을 제한할 수 있으므로, 적절한 상황에서만 사용해야 합니다.
다형성(Polymorphism)의 실전 활용
다형성은 동일한 인터페이스를 통해 다양한 객체가 각자의 방식으로 응답할 수 있는 능력을 의미합니다. 다형성은 코드의 유연성과 확장성을 크게 향상시킵니다.
실제 예제: 도형 그리기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
// 부모 클래스
class Polygon {
public void render() {
System.out.println(“Rendering Polygon…”);
}
}
// 자식 클래스들
class Square extends Polygon {
@Override
public void render() {
System.out.println(“Rendering Square…”);
}
}
class Circle extends Polygon {
@Override
public void render() {
System.out.println(“Rendering Circle…”);
}
}
// 다형성 활용
public class Main {
public static void main(String[] args) {
// 다형적 변수 선언
Polygon polygon = new Polygon();
Polygon square = new Square();
Polygon circle = new Circle();
// 동일한 메서드 호출, 다른 결과
polygon.render(); // “Rendering Polygon…”
square.render(); // “Rendering Square…”
circle.render(); // “Rendering Circle…”
// 다형성을 활용한 메서드
renderShape(new Square());
renderShape(new Circle());
}
public static void renderShape(Polygon shape) {
shape.render();
}
}
|
cs |
다형성의 두 가지 유형
유형 | 설명 | 예시 |
---|---|---|
메서드 오버라이딩 | 자식 클래스에서 부모 클래스의 메서드를 재정의 | 위 예제의 render() 메서드 |
메서드 오버로딩 | 같은 이름의 메서드를 다른 매개변수로 정의 | add(int a, int b) , add(double a, double b, double c) |
캡슐화(Encapsulation)의 실전 활용
캡슐화는 데이터와 메서드를 하나로 묶고, 외부에서의 접근을 제한하는 것입니다.
이를 통해 데이터 무결성을 보호하고 코드의 유지보수성을 향상시킵니다.
실제 예제: 직원 정보 관리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public class Employee {
// 비공개 변수 (데이터 은닉)
private int ssn;
private String empName;
private int empAge;
// Getter와 Setter 메서드
public int getEmpSSN() {
return ssn;
}
public String getEmpName() {
return empName;
}
public int getEmpAge() {
return empAge;
}
public void setEmpAge(int newValue) {
if (newValue > 0 && newValue < 120) { // 유효성 검사
empAge = newValue;
} else {
throw new IllegalArgumentException(“Invalid age value”);
}
}
public void setEmpName(String newValue) {
if (newValue != null && !newValue.isEmpty()) {
empName = newValue;
} else {
throw new IllegalArgumentException(“Name cannot be empty”);
}
}
public void setEmpSSN(int newValue) {
// 실제로는 더 복잡한 유효성 검사가 필요
ssn = newValue;
}
}
|
cs |
캡슐화의 이점
이점 | 설명 |
---|---|
데이터 보호 | 직접 접근을 제한하여 데이터 무결성 유지 |
유효성 검사 | setter 메서드에서 입력 데이터 검증 가능 |
구현 변경 유연성 | 내부 구현 변경 시 외부 코드에 영향 최소화 |
사용 편의성 | 복잡한 내부 로직을 간단한 인터페이스로 제공 |
추상화(Abstraction)의 실전 활용
추상화는 복잡한 시스템에서 핵심적인 개념이나 기능을 간추려내는 과정입니다.
자바에서는 추상 클래스와 인터페이스를 통해 추상화를 구현합니다.
실제 예제: 동물 소리 내기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
// 추상 클래스
abstract class Animal {
// 추상 메서드 (구현 없음)
public abstract void makeSound();
// 일반 메서드
public void sleep() {
System.out.println(“Zzz…”);
}
}
// 구체 클래스
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println(“Woof”);
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println(“Meow”);
}
}
// 추상화 활용
public class Main {
public static void main(String[] args) {
// Animal 직접 인스턴스화 불가
// Animal animal = new Animal(); // 오류!
Animal dog = new Dog();
Animal cat = new Cat();
dog.makeSound(); // “Woof”
dog.sleep(); // “Zzz…”
cat.makeSound(); // “Meow”
cat.sleep(); // “Zzz…”
}
}
|
cs |
추상화 구현 방법
방법 | 특징 | 사용 시기 |
---|---|---|
추상 클래스 | 일부 구현 제공 가능, 단일 상속만 가능 | 관련 클래스들의 공통 기능 구현 시 |
인터페이스 | 메서드 시그니처만 정의, 다중 구현 가능 | 클래스들 간의 계약 정의 시 |
추상화 활용 모범 사례
-
인터페이스에 프로그래밍하라: 구체적인 구현보다 추상 인터페이스에 의존하여 유연성 확보
-
적절한 추상화 수준 선택: 너무 추상적이거나 구체적이지 않은 적절한 수준 유지
-
명확한 책임 분리: 각 추상 클래스나 인터페이스가 단일 책임을 갖도록 설계
실전 종합 예제: 도형 그리기 애플리케이션
다음 예제는 네 가지 원칙을 모두 활용한 종합 예제입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
// 인터페이스 (추상화)
interface Shape {
double getArea();
double getPerimeter();
}
// 추상 클래스 (추상화)
abstract class AbstractShape implements Shape {
private String color;
public AbstractShape(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public void showColor() {
System.out.println(“Color: “ + color);
}
// 추상 메서드
public abstract void draw();
}
// 구체 클래스 (상속, 다형성)
class Circle extends AbstractShape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
@Override
public void draw() {
System.out.println(“Drawing a circle.”);
}
}
class Rectangle extends AbstractShape {
// 캡슐화
private double width, height;
public Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public double getArea() {
return width * height;
}
@Override
public double getPerimeter() {
return 2 * (width + height);
}
@Override
public void draw() {
System.out.println(“Drawing a rectangle.”);
}
}
|
cs |
객체지향 프로그래밍의 네 가지 핵심 원칙인 상속, 다형성, 캡슐화, 추상화는 서로 밀접하게 연관되어 있으며, 함께 사용될 때 가장 큰 효과를 발휘합니다.
이러한 원칙들을 적절히 활용하면 코드의 재사용성, 유지보수성, 확장성을 크게 향상시킬 수 있습니다