프로그래밍 패러다임(programming paradigm)
프로그래머에게 프로그래밍의 관점을 갖게 해주는 역할을 하는 개발 방법론
선언형 프로그래밍: '무엇을' 풀어내는가에 집중하는 패러다임
함수형 프로그래밍: 선언형 패러다임의 하위 집합
작은 순수 함수들을 조합하고, 고차 함수를 활용해 재사용성을 높임
const list = [1, 2, 3, 4, 5, 11, 12]
const ret = list.reduce((max, num) => num > max ? num : max, 0)
console.log(ret) // 12
reduce()는 입력에 따라 결과만 반환하는 순수 함수의 예
출력이 입력에만 의존
const pure = (a, b) => {
return a + b
}
pure 함수는 들어오는 매개변수 a, b에만 영향을 받음
만약 a, b 말고 다른 전역 변수 c 등이 이 출력에 영향을 주면 순수 함수가 아님
함수를 인자로 받거나 반환하는 함수
함수형 프로그래밍은 이외에도 커링, 불변성 등 많은 특징이 있음
객체지향 프로그래밍(OOP, Object-Oriented Programming)
객체들의 집합으로 프로그램의 상호 작용을 표현하며 데이터를 객체로 취급하여 객체 내부에 선언된 메서드를 활용하는 방식
설계에 많은 시간이 소요되며 처리 속도가 다른 프로그래밍 패러다임에 비해 상대적으로 느림
const ret = [1, 2, 3, 4, 5, 11, 12]
class List {
constructor(list) {
this.list = list
this.mx = list.reduce((max, num) => num > max ? num : max, 0)
}
getMax() {
return this.mx
}
}
const a = new List(ret)
console.log(a.getMax()) // 12
클래스 List의 메서드 getMax()로 list의 최댓값을 반환하는 예제
추상화: 복잡한 객체에서 핵심 속성만 추림
캡슐화: 캡슐화: 속성과 메서드를 묶고 일부는 숨김
상속성: 상위 클래스의 특성을 하위 클래스가 이어받음
다형성: 하나의 메서드/클래스가 다양한 방식으로 동작(대표적으로 오버로딩, 오버라이딩)
메서드를 여러 개 두는 것
매개변수의 수나 타입이 다르면 같은 이름의 메서드 정의 가능
class Person {
public void eat(String a) {
System.out.println("I eat " + a);
}
public void eat(String a, String b) {
System.out.println("I eat " + a + " and " + b);
}
}
public class CalculateArea {
public static void main(String[] args) {
Person a = new Person();
a.eat("apple");
a.eat("tomato", "phodo");
}
}
/*
I eat apple
I eat tomato and phodo
*/
매개변수의 개수에 따라 다른 함수가 호출되는 것을 알 수 있음
주로 메서드 오버라이딩(method overriding)을 말하며 상위 클래스로부터 상속받은 메서드를 하위 클래스가 재정의하는 것을 의미
class Animal {
public void bark() {
System.out.println("mumu! mumu!");
}
}
class Dog extends Animal {
@Override
public void bark() {
System.out.println("wal!!! wal!!!");
}
}
public class Main {
public static void main(String[] args) {
Dog d = new Dog();
d.bark();
}
}
/*
wal!!! wal!!!
*/
부모 클래스는 mumu! mumu!로 짖게 만들었지만 자식 클래스에서 wal!!! wal!!!로 짖게 만들었더니 자식 클래스 기반으로 메서드가 재정의됨을 알 수 있음
객체지향 프로그래밍을 설계할 때는 SOLID 원칙을 지켜야함
단일 책임 원칙(SRP, Single Responsibility Principle)
하나의 클래스는 하나의 책임만
A라는 로직이 존재한다면 어떠한 클래스는 A에 관한 클래스여야 하고 이를 수정한다고 했을 때도 A와 관련된 수정이어야 함
개방-폐쇄 원칙(OCP, Open Closed Principle)
확장엔 열려 있고, 수정엔 닫혀 있어야 함
기존의 코드는 잘 변경하지 않으면서도 확장은 쉽게 할 수 있어야 함
리스코프 치환 원칙(LSP, Liskov Substitution Principle)
부모 자리에 자식을 넣어도 동작해야 함
범석 객체가 홍철 객체의 자식 계층일 때 범석 객체를 홍철 객체와 바꿔도 문제가 없어야 하는 것을 말함
인터페이스 분리 원칙(ISP, Interface Segregation Principle)
하나의 일반적인 인터페이스보다 구체적인 여러 개의 인터페이스를 만들어야 함
의존 역전 원칙(DIP, Dependency Inversion Principle)
고수준 모듈이 저수준 모듈에 의존하면 안 됨
상위 계층은 하위 계층의 변화에 대한 구현으로부터 독립해야 함
연속적인 명령(절차)에 따라 로직을 수행
가독성 좋고, 속도가 빠르며 계산이 많은 작업에 적합
const ret = [1, 2, 3, 4, 5, 11, 12]
let a = 0
for(let i = 0; i < ret.length; i++){
a = Math.max(ret[i], a)
}
console.log(a) // 12
단점: 모듈화 어려움, 유지보수 불편
예시
참고: 북스터디 - 면접을 위한 CS 전공지식 노트 (Chapter 1-2)