1. 객체지향 프로그래밍 VS 절차지향 프로그래밍


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

    객체지향 프로그래밍(Object-oriented Programming/OOP)은 컴퓨터 프로그래밍 패러다임 중 하나로, 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다. 초기 프로그래밍 방식은 절차적 프로그래밍 방식이였지만 간단한 알고리즘이면 모를까 조금만 복잡해지면 순서도로 나타내는 것이 불가능할 정도의 복잡한 코드(이를 스파게티 코드라고 한다.)가 만들어지고 이는 다른 사람이 보기는 물론이고 작성자 또한 유지보수가 어려워 이를 보완하기 위해 만들어졌다.

  • 객체(Object)와 클래스(Class) ?

    • 객체(Object) : 객체란 물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 자신의 속성을 가지고 있고 다른것과 식별이 가능한 것을 말한다. 속성과 동작으로 구성되어 있으며 자바에서는 필드(Field)와 메서드(Method)라고 불린다.
    • 클래스(Class) : 클래스란 객체의 상태를 나타내는 필드(Field)와 메서드(Method)로 구성된다. 즉, 필드란 클래스에 담겨있는 변수를 의미한다.
  • 객체지향 프로그래밍의 장점과 단점

  • 객체지향 프로그래밍의 장점

  1. 코드 재사용이 용이하다.
    => 남이 만든 클래스를 가져와서 이용할 수 있고 상속을 통해 확장해서 사용할 수 있다.

  2. 유지보수가 쉽다.
    => 절차지향 프로그래밍에서는 코드를 수정해야할 때 일일이 찾아 수정해야하는 반면 객체지향 프로그래밍에서는 수정해야 할 부분이 클래스 내부에 멤버 변수 혹은 메스드로 존재하기 때문에 해당 부분만 수정하면 된다.

  3. 대형 프로젝트에 적합하다.
    => 클래스 단위로 모듈화시켜서 개발할 수 있으므로 대형 프로젝트처럼 여러 명, 여러 회사에서 프로젝트를 개발할 때 업무 분담하기가 쉽다.

  • 객체지향 프로그래밍의 단점
  1. 절차지향보다 속도가 느리다.
  2. 설계에 필요한 시간이 늘어난다.
  3. 객체가 많아지면 그만큼 용량이 커진다.
  • 절차지향 프로그래밍이란?

    절차지향 프로그래밍(Procedural Programming)이란 물이 위에서 아래로 흐르듯 순차적으로 처리하는 프로그래밍 방법이다. 코드를 유기적으로 연결하여 순서대로 작성하며, 대표적으로 C언어가 있다.

  • 절차지향 프로그래밍의 장점과 단점

  • 절차지향 프로그래밍의 장점
  1. 컴퓨터의 처리구조와 유사하여 실행 속도가 빠르다.
  • 절차지향 프로그래밍의 단점
  1. 유지보수가 어렵다
    => 모든 구성요소가 유기적으로 연결되어 있어서 어느 한 부분에서 버그가 발생한다면 모든 시스템에 버그가 생기기 때문에 시스템 전부를 수리해야 할 수도 있다.
  2. 순서가 정해져 있어서 비효율적이다.
    => 실행 순서가 정해져있기 때문에 코드의 순서가 바뀌게 되면 결과값이 바뀔 수도 있다.

2. 객체지향 프로그래밍의 4대 특성


1. 추상화(Abstraction) : 어떤 영역에서 필요로 하는 속성이나 행동을 추출하는 작업

  • 사물들의 공통된 특징, 즉 추상적 특징을 파악해 대상으로 삼은 행위
  • 구체적인 사물들의 공통적인 특정을 파악해서 이를 하나의 개념(집합)으로 다루는 수단을 말한다.
  • 각 개체의 구체적인 개념에 의존하지 말고 추상적 개념에 의존해야 설계를 유연하게 변경할 수 있다.
    //구체적 개념에 의존한 경우
    switch(자동차 종류) {
    	case 아우디 : //아우디가 움직이는 메서드 생성
        case 벤츠 : //벤츠가 움직이는 메서드 생성
        case BMW : //BMW가 움직이는 메서드 생성
    }
    //추상적 개념에 의존한 경우
    public class Car {
    	String name;
        public Car(String name) {
        	this.name = name;
        }
        void moveCar(Car c) {
        	c.moveCar(); //추상 메서드
        }
     }

2. 캡슐화(Encapsulation) : 기능별로 연관된 속성과 메서드를 객체에 담아 묶는 작업

  • 실제로 구현 부분을 외부에 드러나지 않도록 하는것
  • 변수와 메서드를 하나로 묶음
  • 데이터를 외부에서 직접 접근하지 않고 함수를 통해서만 접근
    public class Car {
    	String name;
       	public Car(String name) {
        	this.name = name;
        }
        public void moveCar() {
        	//자동차 움직이는 메서드 작성
            System.out.println("부릉부릉~!");
        }
    }
 => 차와 관련된 속성(변수)와 메서드를 하나의 묶음으로 만드는 것.(=캡슐화)

3. 상속(Inheritance) : 공통되는 속성이나 메서드를 일일이 작성하지 않고 상속을 통해 하나만 작성해 사용하는 것.

  • 자식 클래스가 부모 클래스의 특성과 기능을 물려받는 것.
  • 기능의 일부분을 변경하는 경우 자식 클래스에서 상속받아 수정 및 사용함.
  • 상속은 캡슐화를 유지, 클래스의 재사용이 용이하도록 한다.
    public class Car {
    	String name;
       	public Car(String name) {
        	this.name = name;
        }
        public void moveCar() {
        	//자동차 움직이는 메서드 작성
            System.out.println("부릉부릉~!");
        }
    }
    class 아우디 extends Car{
    }
    class 벤츠 extends Car{
    }
    class BMW extends Car{
    }
=> Car라는 부모 클래스를 만들어서 아우디,벤츠,BMW 클래스에 상속시켜 중복 코드 작성을 줄여준다.

4. 다형성(Ploymrophism) : 하나의 메서드가 다양한 형태를 갖고 있을 수 있다.

  • 어떤 변수, 메서드가 상황에 따라 다른 결과를 내는 것을 의미한다.
    + Overloading(오버로딩) : 하나의 클래스에서 메서드의 이름이 같지만, 파라미터가 다른 것.
    + Overriding(오버라이딩) : 부모 클래스의 메서드를 자식 클래스의 용도에 맞게 재정의하여 코드를 재사용 하는것.
    public class Car {
    	String name;
       	public Car(String name) {
        	this.name = name;
        }
        public void moveCar() {
        	//자동차 움직이는 메서드 작성
            System.out.println("부릉부릉~!");
        }
        public void brake() {
        	System.out.println("끼익!")
        }
    }
    class 아우디 extends Car{
    	@Override
        public void brake() {
        	System.out.println("꺄아악!");
        }
    }
    class 벤츠 extends Car{
        @Override
        public void brake() {
        System.out.println("드르르르ㅡ럭!");
        }
    }
    class BMW extends Car{
    	@Override
        public void brake() {
        	System.out.println("꾸아아아악!");
        }
    }
=> 이처럼 Car라는 부모 클래서에 정의되어있는 brake를 자식 클래스에서 재사용 하고 있다.

3. 객체지향 프로그래밍의 5원칙(SOLID)


S (SRP : Single Responsibility Principle)

  • 하나의 모듈은 한 가지 책음을 가져야 한다는 것.
class Car {
	String name;
	public void moveCar() {
    	if(name == "아우디") {
        	this.canMakeOpenCar = true;
        }else if(name == "벤츠") {
        	this.canMakeOpenCar = true;
            this.brake = true;
        }
    }
}
//=> 이처럼 하나의 메서드에 복수 메서드가 포함되어있다면 S에 위배된다. 
//또한 다른 차가 등장한다면 else if로 일일이 추가해야 하고 moveCar 메서드에 에러가 생긴다면
//모든 종류의 차에 moveCar메서드가 실행되지 않는다.
class 아우디 extends Car() {
	public void moveCar() {
    	this.canMakeOpenCar = true;
    }
}
class 벤츠 extends Car() {
	public void moveCar() {
    	this.canMakeOpenCar = true;
        this.brake = true;
    }
}
//이처럼 분리를 하게되면 S에 위배되지 않는다. 즉, 하나의 클래스에 하나의 책임이 부여된다.
//더 많은 차가 추가되더라도 그 차에 대한 클래스만 추가해주면 된다.
//아우디에 문제가 생기더라도 다른 종류의 차에는 영향을 끼치지 않는다

O (OCP : Open Closed Principle)

  • 자기 자신의 확장에는 열려있지만 주변의 변화에는 닫혀있어야 한다는 의미.
public class Car {
	String name;
   	public Car(String name) {
    	this.name = name;
    }
    public void moveCar() {
    	//자동차 움직이는 메서드 작성
        System.out.println("부릉부릉~!");
    }
    public void brake() {
    	System.out.println("끼익!")
    }
}
class 아우디 extends Car{
	@Override
    public void brake() {
    	System.out.println("꺄아악!");
    }
}
class 벤츠 extends Car{
    @Override
    public void brake() {
    System.out.println("드르르르ㅡ럭!");
    }
}
class BMW extends Car{
	@Override
    public void brake() {
    	System.out.println("꾸아아아악!");
    }
}
//예를 들어 아우디의 brake 메서드가 "끄갸갸갸갸갸갸갹"으로 바뀐다고 해서 다른 차인 벤츠나 BMW
//의 brake 메서드가 영향을 받으면 안된다는 의미이다. 

L (LSP : Liskov's Substitution Principle)

  • 부모 객체를 상속받은 자식 객체는 항상 부모 타입으로 치환이 가능해야한다는 의미.
public class Car {
	String name;
   	public Car(String name) {
    	this.name = name;
    }
    public void moveCar() {
        System.out.println("부릉부릉~!");
    }
}
class 아우디 extends Car{
}
class 벤츠 extends Car{
}
class BMW extends Car{
}
// Car c = new 아우디(); 가 가능해야한다는 것이다.-Upcasting
// 아우디 au = (Car) new Car(); 는 오류가 발생할 가능성이 있다.-Downcasting 

+Upcasting과 Downcasting

Upcasting : 자식 객체를 부모 타입으로 치환.(자동으로 이루어짐)
=> 위 코드에서 Car라는 부모 객체를 아우디 라는 자식 객체로 치환할 때 이루어진다.
Downcasting : 부모 객체를 자식 타입으로 치환.(직접 치환 해주어야 하며 오류발생 가능성이 있다.)
=> 위 코드에서 아우디 라는 자식 객체로 Car라는 부모 객체를 치환할 때 이루어진다.

I (ISP : Interface Segregation Principle)

  • 사용하지 않는 인터페이스는 구현하지 말아야 한다는 의미
public class Car {
	String name;
    public void driving() {}
    public void brake() {}
    public void cleaning() {}
}
class 아우디 extends Car{
	public void driving() {}
}
class 벤츠 extends Car{
	public void driving() {}
    public void brake() {}
}
class BMW extends Car{
    public void brake() {}
}
//위를 보면 Car클래스 안에 있는 cleaning은 어떤 클래스도 사용하고 있지 않다.
//이처럼 필요없는 인터페이스는 구현하지 않아야한다.

D (DIP : Dependency Inversion Principle)

  • ㅇ의존 관계(상속 관계)를 맺을 때, 자신보다 변하기 쉬운 것에는 의존하지 말아야 한다는 의미
public class hydrogenCar() {
	String name;
    public void driving() {}
    public void brake() {}
}
class 아우디 extends hydrogenCar{
	public void driving() {}
}
// 이처럼 아우디에서는 수소차만 있는것이 아니라 전기차, 휘발유차같은 다양한 종류의 차를 출시 할 수
// 있다. 이처럼 변화가 잦은 클래스에 의존하지 말아야한다는 것이다.
profile
개발을 꿈꾸는 초짜

0개의 댓글