객체지향 프로그래밍 대체 뭔데

김운채·2023년 5월 5일
0

Java

목록 보기
9/11

객체지향 프로그래밍이란??

객체지향 프로그래밍(Objct Oriented Programming)이란, 프로그램 구현에 필요한 객체를 파악하고 각각의 객체들의 역할이 무엇인지를 정의하여 객체들 간의 상호작용을 통해 프로그램을 만드는 것이다.

1-1. 객체지향 프로그래밍의 장점

✅ 유연한 프로그래밍
가장 큰 장점은 객체 지향적 설계를 통해서 프로그램을 보다 유연하고 변경이 용이하게 만들 수 있다는 점이다.
마치 컴퓨터 부품을 갈아 끼울 때, 해당하는 부품만 쉽게 교체하고 나머지 부품들을 건드리지 않아도 되는 것처럼, 객체지향 프로그래밍은 각각의 부품들이 각자의 독립적인 역할을 가지기 때문에 코드의 변경을 최소화하고 유지보수를 하는 데 유리하다.

✅ 코드의 재사용
기존에 만들어진 클래스를 재사용할 수 있고 상속을 통해 확장을 하는 등의 행위로 코드의 재사용을 통해 반복적인 코드를 최소화하고, 코드를 최대한 간결하게 표현할 수 있다.

1-2. 객체(object)란?

객체는 값을 저장 할 변수와 작업을 수행 할 메소드를 서로 연관된 것들끼리 묶어서 만든 것을 객체라고 할 수 있다.
프로그래밍에서의 객체는 클래스에 정의된 내용대로 메모리에 생성된 것을 말한다.

객체 지향 프로그래밍에서는 각각의 객체를 추상화시켜 속성(state)과 기능(behavior)으로 분류한 후에 이것을 다시 각각 변수(variable)와 함수(function)로 정의하고 있다.

이러한 객체를 생성하는데 사용되는 것이 클래스이다. 클래스란 객체를 정의해 놓은 것이라고 할 수 있다.


출처

❓ 그렇다면 클래스는 객체일까?
👉 클래스는 객체를 생성하는데 사용될뿐, 객체 자체는 아니다!
객체란 실체를 가지고 있는 것 ex) 너랑 나 => 객체
클래스는 어떤 종류(타입) 이라고 이해하면 좋다. ex) 너랑 나는 '사람'(타입)

1-3. 객체지향 프로그래밍의 특징

캡슐화, 추상화, 상속, 다형성 이렇게 4가지의 특징을 가지고 있다.

(1) 캡슐화

캡슐화는 데이터 그리고 데이터를 활용하는 함수를 캡슐(class) 혹은 컨테이너 안에 두는 것을 의미한다. (변수와 함수를 하나로 묶는 것)

이로써 원하는 데이터는 숨길 수 있고 노출할 자료와 숨길 자료를 선택할 수 있게된다. 즉, 객체 외부에서는 개체 내부 정보를 직접 접근하거나 조작할 수 없고, 외부에서 접근할 수 있도록 정의된 오퍼레이션을 통해서만 관련 데이터에 접근할 수 있다.(Getter, Setter)

ㅇㅋㅇㅋ 속성과 행위를 하나로 묶고(캡슐화) 구현 내용을 외부에 감춘다는 것(정보은닉)은 이제 알겠다.
근데 속성과 행위를 하나로 묶는 것, 외부에 정보를 감추는것, 이것들이 대체 왜 중요한걸까?

예시와 같이 이해해보자.
다음과 같이 상품의 10% 할인된 금액을 구하는 로직이 있다.

public void shop(Product product){
	double discountPrice = product.getPrice() * 0.9;
    var(discountPrice)
}

이 로직을 로직에서도 사용해야 된다면 다시 똑같은 코드를 작성하거나, 코더가 다른 경우, 다른 방법으로 10% 할인 금액을 구해서 코드를 작성할 수도 있다. => 코드의 중복이 일어났다!

만약 10% 할인되는 금액을 구하는 것이 아니라 요구사항이 20% 할인된 금액으로 바뀌었다면, 우리는 중복된 코드, 파편화된 코드를 다 일일히 찾아서 바꿔줘야 할 것이다.

그럼 여기까지 코드의 중복이 안좋다는 것을 알았다. 그럼 외부에 정보를 감추는 것은 왜 중요할까??

그럼 다시 코드를 고쳐보자

class Product {
	int price = 10000;
    ...
    public int getDiscountPrice(){
    	return price * 0.9;
    }
}

public void shop(Product product){
	double discountPrice = product.getDiscountPrice();
    var(discountPrice)
}

10% 할인된 금액을 가져오는 로직이 객체 안으로 이동했다. 이제 shop()에서 10%프로를 할인하는게 아닌, Product의 메서드를 호출해서 Product 에서 처리되도록 변경됐다.

그럼 이제 다시 요구사항 변경 문제로 가보자. 20%로 할인금액이 바뀌어도 이제는 getDiscountPrice()의 로직을 수정하면 된다. 객체 내부의조작 방법이 바뀌어도 외부에서 호출하는 방법은 바뀌지 않는다. 데이터 처리를 외부가 아닌, 객체 자율적으로 처리되도록 하니 문제가 간단하게 해결됐다.

우리가 커피를 내릴때, 커피머신 사용법은 알지만 커피머신의 구동원리를 몰라도 되는 것처럼, 이렇게 캡슐화된 api를 우린 호출만 해서 갖다 쓰면 되는 것이다.
뭣도 모르고 커피머신 부품하나 임의로 바꿨다가 머신 자체에 문제가 생길 수도 있으니, 접근제어하여 외부에서 임의로 바꾸지 못하게 막아 놓는 것이다.

(2) 추상화

추상화는 불필요한 정보는 놔두고, 중요한 정보만을 표시함으로써 공통의 속성이나 기능을 묶어 이름을 붙이는 것 이라고 할 수 있다. 컴퓨터 과학에서 추상화는 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것을 말한다.

ex) 추상화로 자동차를 구현해놓으면 다른곳의 코드를 수정할 필요없이 추가로 만들부분만 생성해주면 된다.


출처

'자동차' 라는 추상화 집합에는 자동차들이 가진 공통적인 특징들을 넣어서 활용한다.

(3) 상속

상속은 부모의 기능을 자식이 물려받아 확장하는 것 이다.
부모 클래스의 속성과 기능을 그대로 물려받아 사용할 수 있고, 기능의 일부분을 변경해야 할 경우 상속받은 자식 클래스에서 해당 기능만 다시 수정하여 사용할 수 있다.

상속으로 클래스들 간 공유하는 속성과 기능들을 반복적으로 정의할 필요 없이 딱 한 번만 정의해두고 간편하게 재사용할 수 있어 반복적인 코드를 최소화하고 공유하는 속성과 기능에 간편하게 접근하여 사용할 수 있게 된다.

예를 들어서 자동차와 오토바이의 기능과 속성을 보면 다음과 같이 정리할 수 있다.


출처

위 그림에서는 빨간색은 공통속성, 푸른색은 각각의 고유 속성이다.
코드로 표현하면

public class Car {
	String model;
    String color;
    
    voud moveForward(){
    	System.out.println("앞으로");
    }
    voud moveBackward(){
    	System.out.println("뒤로");
    }
    
    //Car 클래스의 고유 속성
    void openWindow(){
    	System.out.println("창문열기");
    }
}
public class MotorBike {
	String model;
    String color;
    
    voud moveForward(){
    	System.out.println("앞으로");
    }
    voud moveBackward(){
    	System.out.println("뒤로");
    }
    
    //MotorBike 클래스의 고유 속성
    public void stunt(){
    	System.out.println("묘기 부리기");
    }
}

이렇게 벌써 공통된 속성들이 반복되고 있는 것이 보인다.
이 공통된 특성들을 뽑아내서 Vehicle 클래스를 만들어보자.

public class Vehicle{
	String model;
    String color;
    
    voud moveForward(){
    	System.out.println("앞으로");
    }
    voud moveBackward(){
    	System.out.println("뒤로");
    }
}

그럼 Car 이랑 MotorBike 클래스는 Vehicle 을 상속받아서 속성과 기능을 그대로 물려받거나, 오버라이딩하여 다시 재정의 해서 쓸 수 있다.

public class Car {
	boolean isConvertable;
    
    void openWindow(){
    	System.out.println("창문열기");
    }
}
public class MotorBike {
	boolean isRaceable;

    public void stunt(){
    	System.out.println("묘기 부리기");
    }
    
    //메서드 오버라이딩 => 기능 재정의
    @Override
    public moveForward(){
    	System.out.println("오토바이 앞으로")
    }
}
public class Main {
	public static void main(String[] args){
    	Car car = new Car();
        MotorBike motorBike = new MotorBike();
        
        car.model ="벤츠"
        
        System.out.println("내 차는 "+ car.model);
        
        car.moveForward();
        motorBike.moveForward();
    }
}
//결과
내 차는 벤츠
앞으로
오토바이 앞으로

(4) 다형성

다형성(多形性)이란 한자 이름 그대로 어떤 객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있는 성질을 의미한다.

앞서 본 상속 예제 처럼, 메서드 오버라이딩을 사용하면 같은 이름의 메서드를 각각의 클래스에 맞게 재정의 하여 사용할 수 있다.
또한, 하나의 클래스 내에서 같은 이름의 메서드를 여러 개 중복하여 정의하는 것을 의미하는 메서드 오버로딩도 이와 같은 맥락이다.

하지만 객체지향에서의 다향성의 정의 이것에 좀더 집중되어 있다.

객체 지향 프로그래밍에서 다형성이란 한 타입의 참조변수를 통해 여러 타입의 객체를 참조할 수 있도록 만든 것을 의미합니다. 좀 더 구체적으로, 상위 클래스 타입의 참조변수로 하위 클래스의 객체를 참조할 수 있도록 하는 것입니다.

뭔말인지 당최 모르겠다. 간단한 예제를 보면서 이해해보자.

Fruit 이라는 과일 클래스를 만들었다.

public class Fruit{
	String name;
    int price;
    
    public void show(){
    	System.out.println("이름 : "+name);
        System.out.println("가격 : "+price);
    }
}

그 다음 Peach 클래스 만들어 정보를 넣어주었다.

public clss Peach extends Fruit{
	//변수초기화
	public Peach(){
    	name = '복숭아'
    	price = 1500
    }
}

그럼 이렇게 Main 클래스에서 peach 의 인스턴스를 fruit의 변수안에 넣을 수 있다.

public class Main {
	public static void main(String[] args){
    	Fruit fruit = new Peach();
        fruit.show();
    }
}
이름 : 복숭아
가격 : 1500

Peach 말고도 Banana, Apple 등 다양한 과일 종류 클래스를 만들어도 손쉽게 인스턴스 변수를 쉽게 바꿔줄 수 있는 것이다. 과일 종류가 무엇으로 바뀌던지 간에 내가 따로 들어가서 정보를 하나하나 바꿀 필요없이 객체만 바꿔주면 된다는 것이다. 이로써 좀 더 편리하고 유연하게 변경에 대응할 수 있는 프로그래밍을 할 수 있게 된다.

0개의 댓글