객체지향 프로그래밍이란 무엇인가?

- 객체지향 프로그래밍은 문제를 객체로 나누어 해결하는 철학을 기반으로 합니다. 각 객체는 자신만의 데이터를 가지고 있으며, 다른 객체와 협력하여 전체 시스템을 구성합니다. 한마디로 현실 세계를 소프트웨어로 옮겨와 자연스럽게 문제를 해결하기 위한 패러다임입니다.

객체지향 프로그래밍의 등장

-  앨런케이는 객체지향 프로그래밍의 개념을 널리 알리고 발전시키는데 큰 역할을 한 컴퓨터 과학자입니다. 그는 Smalltalk라는 언어을 설계하여 객체지향 개념을 구체화하고, OOP가 현대 프로그래밍의 중요한 패러다임으로 자리잡는데 기여했습니다.

앨런 케이의 객체지향 프로그래밍 철학에서 중요한 개념은 세포비유입니다.
그는 객체지향이 사람의 세포와 같이 독립적인 단위들이 서로 협력하여 시스템을 구성하는 방식이라는 강조하였습니다.
객체는 세포처럼 독립적이고 자율적이며, 메세지를 통해 협력하면서 복잡한 시스템을 구성합니다.

 

엘런 케이의 주장

객체란 독립적이고 협력하는 존재다.

  • 객체는 독립된 실체로서 자체적인 상태와 동작을 가진다.
  • 각 객체는 메시지를 주고받으며 협력하여 작업을 수행한다.
  • 컴퓨터는 네트워크를 가진 세포들의 공동체처럼 작동해야 한다.

메시지 기반 프로그래밍

  • 객체지향의 핵심은 객체들이 메시지를 통해 상호작용하는 방식입니다.

캡슐화

  • 객체는 내부 구현을 외부에 드러내지 않고, 오직 메시지를 통해 접근하게 해야한다.
  • 이는 결합도를 낮추고, 독립성을 유지하도록 설계를 하기 위함입니다.

사용자 친화성과 직관성

  • 프로그래밍이 전문가 뿐만 아니라 비 전문가도 쉽게 접근할 수 있어야 한다고 주장했습니다.
  • Smaltalk는 직관적이고 단순한 문법을 가지도록 설계되었습니다.

객체지향 프로그래밍 주요 개념

 

객체 (Object)

  • 객체는 데이터(속성)와 동작(행동)을 포함하는 독립적인 단위입니다.
  • 데이터와 메서드를 포함하는 독립적인 단위로, 인스턴스와 동일하게 사용됩니다.
  • 소프트웨어에서 현실 세계의 사물을 표현하는 단위입니다.

 

클래스 (Class)

  • 클래스는 객체를 생성하기 위한 설계도 입니다.
  • 객체는 클래스를 기반으로 만들어진 실제 인스턴스 입니다.
  • 속성 : 객체가 가지는 데이터 (멤버 변수)
  • 메서드: 객체가 수행할 수 있는 동작 (멤버 함수)
class Car {
    String brand;    // 속성
    String color;

    void drive() {   // 메서드
        System.out.println(brand + " is driving");
    }
}

 

인스턴스 (Instance)

  • 클래스를 기반으로 생성된 구체적인 객체입니다.
  • 클래스의 설계를 실제로 구현한 개체로, 고유의 데이터를 가질 수 있습니다.
Car car1 = new Car();  // Car 클래스의 인스턴스 생성
car1.brand = "Toyota"; 
car1.color = "Red";

Car car2 = new Car();  // 또 다른 인스턴스 생성
car2.brand = "Honda"; 
car2.color = "Blue";

 

추상화 (Abstraction)

  • 복잡한 내부 구현을 숨기고, 필요한 부분만 외부에 공개하는 원리입니다.
  • 사용자 입장에서는 필요한 기능만 보게 하고, 내부 세부 사항은 숨깁니다.
abstract class Animal {
    abstract void makeSound();  // 추상 메서드
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Bark");
    }
}

 

인터페이스 (Interface)

  • 클래스가 구현해야할 행동의 규약을 정의한 추상 타입입니다.
  • 구현 클래스가 어떤 메서드를 가져야 하는지 강제합니다.
  • 메서드 선언만 포함하며, 구현은 제공하지 않고 다중상속을 지원합니다.
interface Animal {
    void makeSound();  // 구현 필요
}

class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow");
    }
}

 

객체지향의 5대 개념

클래스

  • 속성과 행동을 정의하며, 이를 통해 객체를 생성합니다.
package org.example.User;

public class User {

    private String name;

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

    public String getName() {
        return name;
    }

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

 

객체

  • 속성과 메서드를 가지며, 소프트웨어에서 현실 세계의 개체를 표현합니다.
package org.example;

import org.example.User.User;

public class Main {
    public static void main(String[] args) {
        User person = new User("study_youth"); // 객체 생성
    }
}

 

캡슐화

  • 데이터를 외부에서 보호하고, 필요한 부분만 외부에 노출합니다.
  • 캡슐화는 객체의 상태(데이터)를 private으로 숨기고, 그 상태에 접근하거나 수정할 수 있는 메서드를 제공하는 것입니다.
  • 이를 통해 데이터 보호와 무결성을 유지할 수 있습니다.
private String name;

 

상속

  • 기존 클래스(부모 클래스)를 기반으로 새로운 클래스(자식 클래스)를 생성합니다.
package org.example.User;

public class VipUser extends User{

    public VipUser(String name, String grade) {
        super(name);
        this.grade = grade;
    }

    private String grade;

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

 

다형성

  • 같은 메서드 호출이 객체에 따라 다르게 동작합니다.
  • 인터페이스나 상속을 통해 구현됩니다.
public class User {

    private String name;

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

    public String getName() {
        return name;
    }
}

public class GoldUser extends User{
    public GoldUser(String name) {
        super(name);
    }

    public String getGrade(){
        return "Gold";
    }
}

public class VipUser extends User{

    public VipUser(String name) {
        super(name);
    }

    public String getGrade() {
        return "VIP";
    }
}


public class Main {
    public static void main(String[] args) {
        User person = new User("study_youth");
        System.out.println(person.getName());

        VipUser vip = new VipUser("Jame");
        System.out.println(vip.getGrade());

        GoldUser gold = new GoldUser("Joe");
        System.out.println(gold.getGrade());
    }
}

 

객체지향의 5대 원칙 (SOLID 원칙)

단일 책임 원칙 (Single Responsibility Principle, SRP)

단일책임의 원칙(Single Responsibility Principle, SRP)의 핵심은 "하나의 클래스나 모듈은 하나의 책임만 가져야 한다"는 것입니다.여기서 말하는 책임은 클래스나 모듈이 변경될 이유를 의미합니다. 즉, 클래스나 모듈이 변경되는 이유가 하나여야 한다는 것입니다.

class UserManager:
    def __init__(self, user_data, email_service):
        self.user_data = user_data
        self.email_service = email_service

    def add_user(self, name, email):
        self.user_data.append({"name": name, "email": email})
        self.email_service.send_welcome_email(name, email)  # 이메일 전송은 별도의 책임으로 분리됨

    def remove_user(self, name):
        self.user_data = [user for user in self.user_data if user["name"] != name]
        print(f"User {name} removed.")


class EmailService:
    def send_welcome_email(self, name, email):
        print(f"Sending welcome email to {name} at {email}")

 

UserManager 클래스는 이제 사용자 관리라는 하나의 책임만 가지고 있습니다.

 

개방-폐쇄 원칙 (Open-Closed Principle, OCP)

확장에는 열려있고, 수정에는 닫혀 있어야 합니다.

abstract class Shape {
    public abstract double area();
}

class Circle extends Shape {
    private double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

class Rectangle extends Shape {
    private double width, height;
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    @Override
    public double area() {
        return width * height;
    }
}

class AreaCalculator {
    public double calculateArea(Shape shape) {
        return shape.area();
    }
}

 

리스코프 치환 원칙 (Liskov Substitution Principle, LSP)

자식 클래스는 부모 클래스를 대체할 수 있어야 하며, 자식 클래스에서 부모 클래스의 기능을 확장하되 부모 클래스의 계약을 어지럽히지 않아야 합니다.

class Bird {
    public void fly() {
        System.out.println("Bird is flying");
    }
}

class Sparrow extends Bird {
    @Override
    public void fly() {
        System.out.println("Sparrow is flying");
    }
}

class Ostrich extends Bird {
    @Override
    public void fly() {
        // 잘못된 예시: 타조는 날지 못함
        throw new UnsupportedOperationException("Ostrich can't fly");
    }
}

 

인터페이스 분리 원칙 (Interface Segregation Principle, ISP)

하나의 큰 인터페이스를 여러 작은 인터페이스로 나누어, 필요 없는 메서드를 강요하지 않습니다.

interface Printer {
    void print();
}

interface Scanner {
    void scan();
}

class MultiFunctionPrinter implements Printer, Scanner {
    @Override
    public void print() {
        System.out.println("Printing...");
    }

    @Override
    public void scan() {
        System.out.println("Scanning...");
    }
}

class SimplePrinter implements Printer {
    @Override
    public void print() {
        System.out.println("Printing...");
    }
}

 

의존성 역전 원칙 (Dependency Inversion Principle, DIP)

고수준 모듈이 저수준 모듈에 의존하지 말고, 두 모듈 모두 추상화에 의존하도록 해야 합니다. 또한, 추상화는 구체적인 구현에 의존해서는 안 됩니다.

interface DataAccess {
    void saveData(String data);
}

class DatabaseAccess implements DataAccess {
    @Override
    public void saveData(String data) {
        System.out.println("Data saved to database");
    }
}

class FileAccess implements DataAccess {
    @Override
    public void saveData(String data) {
        System.out.println("Data saved to file");
    }
}

class DataService {
    private DataAccess dataAccess;

    // 의존성 주입
    public DataService(DataAccess dataAccess) {
        this.dataAccess = dataAccess;
    }

    public void save(String data) {
        dataAccess.saveData(data);
    }
}

 

 

  1.  
  1.  

'IT > 백엔드' 카테고리의 다른 글

[ node.js ] - 메모리 관리법  (0) 2025.02.04
객체지향원칙의 책임 중심 설계  (0) 2025.01.21
Garbage Collection 방식  (0) 2024.12.24
[JAVA] - 메모리 관리법  (2) 2024.12.11
[Python] - 메모리 관리법  (2) 2024.12.09

+ Recent posts