Java

자바 OOP(객체 지향 프로그래밍 feat.착각 지향 프로그래밍)

에드박 2020. 7. 19. 04:13

자바의 장점은 역시 객체지향 언어라는 점입니다.

저는 자바를 사용하면서 객체 지향 프로그래밍을 하고있다 생각했습니다.

하지만 책에서 다음과 같은 글을 봤습니다.

 

객체 지향 프로그램의 장점을 얻으려면 단순히 자바나 C++같은 객체 지향 언어로 프로그래밍 하면 되는게 아닌
객체 지향 언어로 프로그래밍 하는것과 더불어 객체 지향 패러다임이 내포된 분석, 설계, 구현, 테스트를 함께 해야합니다.
(분석, 설계, 구현, 테스트에 대해서는 아래쪽에서 설명하겠습니다.)

하지만 단순히 자바를 사용해 프로그래밍을 한다고 객체 지향 프로그래밍의 장점을 얻을 수 있다는 것은 아니라는걸 깨닫고 열심히 객체 지향 프로그래밍을 한다고 생각했던 자신이 부끄러웠습니다.

 

착각 지향 프로그래밍 이었어..?

 

먼저 절차적 프로그래밍 (Procedure Programming) 에 대해 말하자면 자바에서는 함수(method)라고 불리는 것을 이용해서 정리 정돈하는 프로그래밍 기법 이라고 할 수 있습니다.

 

즉 , 메소드를 이용해서 작은 부품을 만들고 이것을 결합해서 더 큰 프로그램을 만들어가는 테크닉이

바로 절차적 프로그래밍 입니다. 대표적인 언어는 C 가 있습니다.

 

하지만 컴퓨터 엔지니어 분들은 메소드만으로 프로그램을 만드는것에 부족함을 느꼈습니다.

 

그래서 서로 연관된 함수(method)와 변수(veriable) 을 모아서 박스를 만들고 거기에 이름을 붙여서 정리정돈을 하고싶어집니다. 그렇게 만들어진 수납상자가 바로 Class 입니다.

이런 클래스를 중심으로 프로그램의 구조를 만들어가는 프로그래밍 방법론을 객체 지향 프로그래밍 이라고 합니다.

 

객체 지향 프로그램은 데이터와 기능이 분리된 구조적 프로그램과는 달리 이들을 묶어 하나의 객체로 만들어 사용합니다.

 

이런 객체 지향 프로그래밍을 언어차원에서 제공하는 언어를 객체지향 언어 라고 합니다.

 

Class 라는 수납상자를  아래와 같이 만들어 볼 수 있습니다.

 

public class Box {

    private String veriable1;
    private String veriable2;

    public void method1() {
    	System.out.println(veriable1);
    }
    
    public void method2() {
    	System.out.println(veriable2);
    }
    
}

 

객체 지향 언어의 장점

- 코드의 재사용성이 용이하다.

- 유지보수가 쉽다.

라는 점이 가장 큰 장점으로 꼽힙니다.

 

객체 지향 프로그래밍 ( Object Oriented Programming ) 이란?

프로그램을 설계하는 개념이자 방법론입니다.

객체지향 프로그래밍의 특징은 크게 4가지 { 캡슐화, 추상화 , 다형성 ,상속성 } 4가지가 있습니다.

 

1. 캡슐화(Encapsulation)

캡슐화란 하나의 객체에 대해 그 객체가 특정한 목적을 위해 필요한 변수나 메소드를 하나로 묶는 것을 의미한다.

예를 들어 우리에게 샐러드를 만들기 위한 레시피가 있습니다.

 

public class Salad {

    private List<Vegetable> vegetables;
    private String dressing;

    public void setVegetables(List<Vegetable> vegetables) { 
    	this.vegetables = vegetables;
    }
    
    public void setDressing(String dressing) {
    	this.vegetables = vegetables;
    }
    
    public void mix() {
    	System.out.println(vegetables + dressing + "샐러드");
    }
    
}

이 샐러드에는 각종 채소들과 소스를 변수로 가지고 재료들을 섞는 mix() 메서드를 가지고있습니다.

이걸로 우리는 훌륭한 샐러드 레시피를 만들었습니다.

 

하지만 샐러드 레시피는 가게의 기밀사항 입니다. 이때 우리는 접근자 라는것을 사용해서 정보를 은닉 해야합니다.

바로 이것이 캡슐화하는 중요한 목적 '정보의 은닉화' 입니다. 

그래서 우리는 재료들을 private 접근자로 선언해서 아무나 접근할 수 없게 하는것입니다.

접근자에 대해서는 다음에 기회가 되면 설명하도록 하겠습니다.

 

이렇게 보호된 재료는 getter 와 setter 메소드를 이용해서 간접적으로 접근하도록 하는 것이 캡슐화의 중요한 목적입니다.

 

2. 추상화(Abstraction)

자바에는 추상 클래스와 인터페이스가 존재합니다.

 

아래 링크에 가시면 추상 클래스와 인터페이스에 대한 내용을 볼 수 있습니다.

 

https://parkadd.tistory.com/3

 

우리는 자동차를 만들 때 현대, 벤츠, 기아, 등등 다른 자동차 회사지만 자동차에는 반드시 있어야하는 기능들이 있습니다. 앞으로 가는 기능, 브레이크, 후진기능, 등등 필수 기능이 있습니다. 이 기능들은 꼭 있어야 하기때문에 이 기능들을 꼭 구현해라고 강제해야합니다. 구현하지 않으면 자동차가 제 기능을 하지 못할겁니다. 이를 위해서 추상화가 있습니다. 설계도에는 이렇게 적혀있습니다.

 

필수기능

  • 앞으로 가는 기능
  • 후진 기능
  • 브레이크

기능들만 적혀있지 어떻게 만들어라는 적혀있지 않습니다. 추상화는 내용은 없지만 여기 적힌 기능들은 만들어야해!(어떻게 만들지는 알아서 해봐) 라고 말하는것이 추상화의 목적입니다. 어떤 클래스를 만들 때 어떤 함수(메소드) 가 있어야해! 라고 말하는 것입니다.

이는 추상 클래스를 상속받거나 인터페이스를 구현함으로써 할 수 있습니다. 상속과 구현은 뒤에 나옵니다.

3.  다형성(polymophism)

다형성은 상속을 통해 기능을 확장하거나 변경하는것을 가능하게 해줍니다. 이를 통해 코드의 재사용, 코드길이의 감소 로 인해 유지보수가 용이하도록 해주는 개념입니다.

 

자바에서 다형성을 녹여낸 부분이 바로 오버라이딩(Overriding) 과 오버로딩(Overloading) 입니다.

 

오버라이딩은 쉽게 말해서 부모의 메서드를 상속받아서 자신의 입맛대로 다시 고쳐서 쓰는것을 말합니다.

 

class Parent {
	public void eat() {
    	System.out.println("찍먹");
    }
}

class Child extend Parent{
    @Override
    public void eat() {
    	System.out.println("부먹");
    }
}

 

오버로딩은 탕수육을 먹는 행위는 똑같이 "먹는다" 라고 부르지만

소스를 누군가는 찍어먹고 누군가는 부어먹을 수 있습니다. 또는 누군가는 "그냥 쳐먹" 이라고 말할 수 있죠.

 

public Class TangSu{

    public void eat() {
        System.out.println("찍먹");
    }

    public void eat(String sauce) {
        System.out.println("부먹");
    }
    
    public String eat() {
    	return "그냥 쳐먹";
    }
    
}

이처럼 같은 eat() 라는 메소드지만 매개변수의 개수나 타입을 다르게 한다던지 , 메소드의 반환타입을 다르게 하는것으로 오버로딩을 구현할 수 있습니다.

 

4. 상속성 (Inheritance)

상속은 객체지향의 꽃이라 부를 수 있습니다.

상속을 통하여 기존 클래스의 기능을 새롭게 만든 클래스에 추가할 수 있음과 동시에 새로운 기능도 추가해서 사용할 수 있습니다.

 

class Parent {
    String name;
    String age;
    
    public void say() {
    	System.out.println(name + age);
    }
    
}

class Child extend Parent{
	String hair;
    
    public void myHair() {
    	System.out.println(hair);
    }
    
}

public class Test() {
	Child c = new Child();
    c.name = "풍성한";
    c.age = 17;
    c.say();
    c.hair = "M자형.."
    c.myHair();
}

 

이처럼 자식 클래스가 부모 클래스의 변수와 함수(method) 까지 사용할 수 있습니다.

 


객체 지향 개발 방법

  • 객체 지향 분석
  • 객체 지향 설계
  • 구현
  • 테스트

 

1.객체 지향 분석(Object Oriented Analysis)

객체 지향 분석은 사용자의 요구를 유스케이스 다이어그램(Usecase Diagram)으로 작성하는 요구 추출과 추출된 요구를 분석하여 클래스 다이어그램(Class Diagram)과 순서 다이어그램(Sequence Diagram)으로 작성하는 요구 분석으로 나눠서 작업됩니다.

1-1. 요구 추출

요구 추출에서는 소프트웨어를 이용할 사용자 즉 액터(Actor)를 찾은 후, 액터와 소프트웨어 사이의 인터페이스 과정을 기술한 시나리오를 작성합니다. 작성된 시나리오를 기반으로 특정 사용자의 사례를 일반화시켜 작성하면 유스케이스가 됩니다.

 

다음 그림은 상품 구매 유스케이스 다이어그램 입니다.

 

상품 구매 유스케이스 다이어그램

1-2. 요구 분석

요구 추출이 끝나면 개발자는 사용자의 요구사항을 어떻게 구현할 것인지 분석합니다. 요구 분석은 다음의 과정을 거칩니다.

 

클래스 / 객체 찾기 클래스 / 객체를 특성별로 구분하여 찾습니다.
클래스 / 객체 사이의
상호 작용 모형화
찾은 클래스 / 객체 사이에 어떤 메시지가 오가는지 파악하여 순서 다이어그램으로 표현합니다.

순서 다이어그램

클래스 / 객체 사이의 연관 관계 찾기 클래스 / 객체 사이에 연관 관계를 클래스 다이어그램으로 표현합니다. 다음 그림은 수강신청 클래스 다이어그램을 나타낸 것입니다. 사각형은 클래스를 의미하고 연결한 선은 관계를 의미합니다.
사각형 내부에는 각 객체의 속성(이름, 상품 이름, 상품 번호 등등)의 필드값과 객체가 수행해야할 기능인 메소드를 포함합니다. 사각형의 내부는 객체 지향 설계 단계에서 합니다.

 

클래스 다이어그램

 

2. 객체 지향 설계

설정한 객체를 구체화 하는 작업입니다. 객체에 속한 속성(필드값)이나 기능(메소드)을 정의합니다.

위의 클래스 다이어그램의 사각형 내부를 정의합니다. 또한 각 객체 사이의 메시지 전달 과정에서 빠진 객체와 기능을 보완해서 재정의 합니다.

 

유저
- 아이디
- 이름
- 이메일
- 비밀번호
- 주소
+getId()
+setId()

 

3. 구현

설계한 클래스 / 객체 상호간의 메시지 전달 과정을 실제로 코딩하는 단계입니다. 객체 지향 개발 방법에서는 디자인 패턴(Design Pattern)과 리팩토링(Refactoring)방법이 대표적인데 이 둘은 소프트웨어를 재활용할 수 있도록 돕습니다.

디자인 패턴은 자주 사용하는 구조를 '프로그래밍 설계 틀' 로 만들어 비슷한 업무에 재사용 할 수 있도록 표준화 한것입니다. 리팩토링은 일단 구현한 뒤 소프트웨어를 유지 보수하기 좋게 다듬어 주는 과정입니다.

 

4. 테스트

  • 메소드 테스트 - 각 객체의 함수나 프로시저를 테스트
  • 클래스 / 객체 테스트 - 개별적인 클래스 / 객체를 테스트
  • 집합 테스트 - 구조적 개발 방법의 통합 테스트
  • 시스템 테스트 - 소프트웨어가 요구사항대로 개발됐는지 테스트

객체 지향 프로그래밍에서는 위의 4단계에 걸쳐 테스트를 진행합니다. 소프트웨어가 웹 기반으로 만들어진 경우라면 웹의 특성을 고려한 테스트가 추가로 필요합니다.