제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
접은 글을 통해 먼저 답변을 해보시고 제가 정리한 답을 확인해보시기 바라겠습니다!!
면접을 위한 CS 전공지식 노트(주홍철 저)을 통해 정리한 포스팅입니다.
면접 리스트
옵저버 패턴은 어떻게 구현하나요?
옵저버 패턴은 객체 간의 일대다 관계를 정의하여, 특정 객체의 상태 변화가 있을 때 그 변화를 다른 객체들에게 자동으로 통지하는 디자인 패턴입니다. 이 패턴은 프록시 객체를 주로 사용합니다. 프록시 객체를 통해 객체의 속성이나 메소드 변화 등을 감지하고 미리 설정해놓은 옵저버들에게 전달하는 방식으로 구현합니다.
구현 방법은 주체가 되는 객체를 Subject라고 하고, 이 객체는 구독자들을 리스트 형태로 관리합니다. 상태에 변화가 생기면 notifyObservers() 메서드를 호출하여 모든 구독자에게 변경 사항을 알립니다. 각 구독자는 Observer 인터페이스를 구현하여 update() 메서드를 정의함으로써 통지를 받고 처리하게 됩니다.
장점으로는 확장성이 뛰어나 새로운 옵저버를 추가해도 주체의 코드에 큰 변경이 필요 없다는 점이 있습니다. 또한, 주체와 옵저버 사이의 결합도가 낮아 객체 간의 독립성을 유지할 수 있는 것이 큰 장점입니다.
단점으로는 옵저버가 많아질 경우 성능 문제가 발생할 수 있으며, 예기치 않은 상태 변화가 복잡하게 일어날 가능성도 있습니다. 또한, 특정 옵저버의 동작이 느릴 경우 전체 알림 체계의 성능에 영향을 미칠 수 있습니다.
이러한 이유로 옵저버 패턴은 유연한 이벤트 기반 시스템에서는 매우 유용하지만, 성능과 관리 측면에서는 주의가 필요합니다.
팩토리 메소드 패턴은 무엇이며 어떠한 장점이 있는가요?
팩토리 메소드 패턴은 객체 생성의 책임을 서브클래스에 위임하는 디자인 패턴입니다. 객체를 생성하는 코드를 캡슐화하여 상위 클래스에서는 객체 생성의 구체적인 클래스를 지정하지 않고, 서브클래스에서 필요한 객체를 생성하도록 하는 것이 특징입니다. 주로 객체 생성의 코드 중복을 줄이고, 코드의 확장성을 높이기 위해 사용됩니다.
장점으로는 새로운 객체 유형을 추가할 때 기존 코드를 거의 변경하지 않고도 새로운 서브클래스를 추가하여 확장성을 높이고 객체 생성 로직을 캡슐화하여 클라이언트 코드의 복잡도를 줄이고 유지 보수성을 높일 수 있습니다. 또한 구체적인 클래스에 의존하지 않으므로 느슨한 결합을 통해 코드 유연성과 재사용성이 증가합니다.
단점으로는 클래스의 수가 많아질 수 있고, 코드 구조가 복잡해질 수 있다는 점이 있습니다. 서브클래스에서 객체 생성을 구현해야 하므로 코드의 가독성이 떨어질 수도 있습니다.
팩토리 메소드 패턴은 특히 다양한 객체를 생성해야 하되, 객체 생성 로직이 복잡하거나 자주 변경되는 경우에 적합한 패턴입니다.
컴포넌트 패턴은 무엇이며 어떠한 장점이 있는가요?
컴포넌트 패턴은 객체를 트리 구조로 구성하여 부분-전체 계층을 표현할 수 있도록 하는 구조적 디자인 패턴입니다. 이 패턴은 개별 객체와 객체 그룹을 동일한 방식으로 다룰 수 있게 하여, 클라이언트가 단일 객체와 복합 객체를 구분하지 않고 일관된 방식으로 처리할 수 있도록 합니다.
장점으로는 단일 객체와 복합 객체를 동일한 인터페이스로 다룰 수 있어 클라이언트 코드가 단순해지며 새로운 타입을 쉽게 추가하여 확장성이 용이합니다. 또 재귀적으로 복합 객체를 구성할 수 있습니다.
단점으로는 복합 구조의 설계가 복잡해질 수 있다는 점입니다. 특히, 트리 구조에서 객체 간의 관계를 잘못 설계하면 객체들이 지나치게 얽혀서 코드의 가독성과 유지보수성이 떨어질 수 있습니다. 또한, 일부 경우에는 단일 객체와 복합 객체의 구분이 모호해질 수 있어 설계 시 주의가 필요합니다.
컴포넌트 패턴은 주로 파일 시스템(파일과 폴더), 그래픽 애플리케이션의 UI 구성 요소, 문서 편집기 등 부분-전체 관계를 나타내야 하는 상황에서 유용합니다.
프록시 서버를 설명하고 사용사례에 대해 설명해보세요
프록시 서버란 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 서버입니다. 주로 애플리케이션 앞단에서 클라이언트의 요청을 처리하여 캐싱, 로깅, 데이터 분석을 하는 서버로 사용합니다. 이를 통해 포트번호를 바꾸어서 실제 서버에 접근하지 못하게 하여 DDOS공격을 차단하며 nginx를 이용해 Node.js로 이루어진 서버의 앞단에 두어 버퍼 오버플로우를 해결할 수 있습니다.
MVC 패턴을 설명하고 MVVM 패턴과 무슨 차이가 있는지 설명해보세요
MVC 패턴은 모델(M), 뷰(V), 컨트롤러(C)로 이루어진 디자인 패턴으로 애플리케이션의 구성요소를 세가지 역할로 구분하여 각각의 요소에 맞는 역할을 분리하여 집중해서 개발할 수 있는 디자인 패턴입니다. MVC 패턴은 재사용성과 확장성이 용이하다는 장점이 있지만, 애플리케이션이 복잡해질 수록 모델과 뷰의 관계 또한 복잡해지는 단점이 있습니다.
MVVM 패턴은 MVC의 C에 해당하는 컨트롤러가 뷰 모델로 바뀐 패턴을 의미합니다. 여기서 뷰모델은 뷰를 더 추상화한 계층이며 MVC 패턴과 다르게 커맨드와 데이터 바인딩을 가지는 것이 특징입니다. 뷰와 뷰모델 사이의 양방향 데이터 바인딩을 지원하며 UI를 별도의 코드 수정없이 재사용할 수 있고 단위 테스팅하기 쉽다는 장점이 있습니다.
TDD는 무엇이며 어떠한 장점이 있는가요?
TDD(Test-Driven Development)는 소프트웨어 개발 방법론 중 하나로, 테스트를 먼저 작성한 후 실제 코드를 구현하는 방식을 의미합니다. 개발자가 특정 기능을 구현하기 전에 해당 기능에 대한 테스트 코드를 작성하고, 이 테스트를 통과할 수 있는 최소한의 코드를 구현한 후 점진적으로 개선해 나가는 반복적인 프로세스를 따릅니다.
TDD는 "Red-Green-Refactor" 사이클을 통해 진행됩니다. 먼저 실패하는 테스트를 작성하여 (Red 단계), 코드가 현재 상태에서 원하는 기능을 충족하지 못함을 확인합니다. 이후, 테스트를 통과하기 위한 최소한의 코드를 작성하여 (Green 단계) 테스트가 성공하도록 만듭니다. 마지막으로 코드를 개선하고 정리하는 리팩토링 단계 (Refactor)를 통해 코드를 최적화하면서도 테스트를 통과할 수 있도록 유지합니다.
TDD는 코드의 품질 보장과 유지보수성 향상에 도움을 주며, 빠른 피드백과 코드 안정성을 높이는 데 기여합니다. 그러나 초기 개발 속도가 느려질 수 있고, 복잡한 로직의 테스트 작성이 어려울 수 있다는 단점도 있습니다.
DDD는 무엇이며 어떠한 장점이 있는가요?
DDD(Domain-Driven Design)는 복잡한 소프트웨어 프로젝트를 개발할 때 비즈니스 도메인, 즉 특정 업무 영역을 깊이 이해하고 이를 중심으로 소프트웨어를 설계하고 개발하는 접근 방식입니다. 도메인은 비즈니스의 문제 영역을 나타내며, DDD는 이 도메인을 철저히 모델링하여 개발자와 도메인 전문가 간의 원활한 소통을 가능하게 하고, 비즈니스 로직을 충실히 반영한 소프트웨어를 구축하는 데 중점을 둡니다.
DDD의 장점으로는 복잡한 비즈니스 로직을 보다 효과적으로 다룰 수 있다는 점이 있습니다. 시스템을 도메인별로 나누어 설계함으로써 모듈성과 확장성이 강화되고, 유지보수와 확장이 용이해집니다. 도메인 경계를 명확히 설정해 모듈 간 의존성을 줄여 결합도를 낮추고, 전체적인 코드의 유지보수성을 높일 수 있습니다.
하지만 DDD는 높은 초기 학습 곡선이 필요하다는 단점이 있습니다. 도메인에 대한 깊이 있는 이해와 설계 패턴에 대한 지식이 요구되며, 이를 완전히 익히고 적용하는 데는 시간이 걸릴 수 있습니다. 또한, 단순한 시스템보다는 복잡한 도메인에 적합하기 때문에, 프로젝트의 규모가 작거나 비즈니스 로직이 단순한 경우에는 오히려 불필요하게 복잡해질 수 있습니다. 도메인을 심도 있게 분석하고 설계하는 과정은 시간과 비용이 많이 들며, 초기 개발 속도가 느려질 가능성도 있습니다.
결론적으로 DDD는 복잡한 비즈니스 로직을 다루는 대규모 시스템 개발에서 매우 유용하며, 도메인 이해와 코드의 유지보수성을 높이는 데 큰 장점을 제공합니다.
객체지향 프로그래밍은 무엇인가요?
객체지향 프로그래밍(OOP, Object-Oriented Programming)은 프로그램을 단순히 함수나 코드의 순서대로 작성하는 것이 아니라, 현실 세계의 객체를 추상화하여 프로그래밍하는 방법입니다. 객체지향 프로그래밍에서는 데이터를 하나의 객체로 묶고, 그 객체가 가진 속성(데이터)과 행위(메서드)를 중심으로 프로그램을 구성합니다. 이를 통해 코드의 재사용성과 확장성을 높이고, 프로그램의 구조를 더 직관적이고 유지보수하기 쉽게 만듭니다.
객체지향 프로그래밍의 핵심 개념에는 클래스와 객체, 캡슐화, 상속, 다형성 등이 있습니다. 클래스는 객체를 생성하기 위한 설계도이며, 객체는 클래스에 의해 생성된 구체적인 실체입니다.
캡슐화는 객체의 속성과 행위를 하나로 묶고, 외부로부터 이를 숨김으로써 데이터의 무결성을 유지하는 개념입니다. 예를 들어, 객체의 내부 상태를 보호하기 위해 외부에서는 객체의 속성에 직접 접근할 수 없고, 메서드를 통해서만 간접적으로 접근하도록 설계할 수 있습니다.
상속은 새로운 클래스가 기존 클래스의 속성과 메서드를 물려받아 재사용하거나 확장할 수 있도록 하는 개념입니다. 이를 통해 코드의 중복을 줄이고, 계층적인 설계를 통해 관리와 유지보수를 쉽게 합니다.
다형성은 같은 메서드나 연산자가 다양한 객체에서 다른 방식으로 동작할 수 있도록 하는 특성입니다.
결론적으로, 객체지향 프로그래밍은 프로그램을 현실 세계의 개념을 반영하여 구성함으로써 구조적이고 이해하기 쉬운 코드를 작성할 수 있게 해주는 강력한 프로그래밍 패러다임입니다.
정리[디자인패턴]
디자인 패턴
💡 라이브러리 VS 프레임워크
라이브러리: 공통으로 사용될 수 있는 특정한 기능을 모듈화 한 것. 코드의 제어권이 라이브러리에 있음.
프레임워크: 공통으로 사용될 수 있는 특정한 기능을 모듈화 한 것. 코드의 제어권이 개발자에 있음.
디자인 패턴
프로그램을 설계할 때 발생했던 문제점들을 객체간의 상호관계 등을 이용하여 해결할 수 있도록 하나의 규약 형태로 만들어 놓은것.
싱글톤 패턴(더 자세한 건 여기로)
하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴. 데이터 베이스 연결 모듈에 많이 사용.(datasource)
- 장점
- 하나의 인스턴스만 있을 때 다른 모듈들이 공유하며 사용하기 때문에 인스턴스 비용이 줄어듦.
- 단점
- 의존성이 높아짐.
Java에서의 싱글톤 패턴
public class HelloSingleton {
static class Singleton {
private static class singleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return singleInstanceHolder.INSTANCE;
}
}
public static void main(String[] args) {
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();
System.out.println("a.hashCode() = " + a.hashCode());
System.out.println("b.hashCode( = " + b.hashCode());
System.out.println("a.equals(b) = " + a.equals(b));
}
}
/*
* a.hashCode() = 1577213552
* b.hashCode( = 1577213552
* a.equals(b) = true
*/
싱글톤 패턴의 단점
- TDD(Test Driven Development)에서의 문제점 발생
- TDD할 때는 단위 테스트 위주.
- 단위 테스트는 테스트를 하는 클래스가 서로 독립적이여야함. => 싱글톤으로의 개발의 복잡성
의존성 주입
- 사용하기 쉽고 실용적이지만 모듈 간의 결합을 강하게 만들 수 있음.
- 의존성 주입(DI, Dependency Injection)을 통해 모듈 간의 결합을 느슨하게 만들 수 있음.
- 의존성 주입자를 통해 메인 모듈이 간접적으로 의존성을 주입하는 방법
- 장점
- 모듈들을 쉽게 교체할 수 있는 구조가 되어 테스팅하기 쉽고 마이그레이션 하기 수월 => 객체지향의 장점
- 구현할 때 추상화 레이어를 통해 구현체를 넣어주기 때문에 의존성 방향이 일관적이고 애플리케이션을 쉽게 추론할 수 있으며 모듈 간의 관계들이 명확
- 단점
- 복잡성 증가
- 원칙
- 상위 모듈은 하위 모듈에서 어떠한 것도 가져오면 안됨.
- 모두 추상화에 의존해야하며 추상화는 세부사항에 의존하면 안됨.
팩토리 패턴(더 자세한 건 여기로)
객체의 생성 부분을 제거하고 추상화한 패턴. 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴
- 클래스가 분리되기 때문에 느슨한 결합을 가지며 더 많은 유연성을 얻게 됨.
- 객체 생성 로직이 따로 있기 때문에 코드를 리팩토링 할 때 한 곳만 고치면 되기에 유지 보수성이 증가함.
- 전달받은 값에 따라 다른 객체를 생성하며 인스턴스의 타입 지정 가능.
enum CoffeeType {
LATTE(0),
ESPRESSO(1),
TEA(2);
private int type;
CoffeeType(int type) {
this.type = type;
}
public int getType() {
return type;
}
}
abstract class Coffee {
protected String name;
public String getName() {
return name;
}
}
class Latte extends Coffee {
public Latte() {
super.name = "Latte";
}
}
class Espresso extends Coffee {
public Espresso() {
super.name = "Espresso";
}
}
public class FactoryPattern {
static class CoffeeFactory {
public static Coffee createCoffee(CoffeeType type) {
switch (type) {
case LATTE -> {
return new Latte();
}
case ESPRESSO -> {
return new Espresso();
}
default -> {
throw new IllegalArgumentException("Invalid coffee type: " + type);
}
}
}
}
public static void main(String[] args) {
Coffee brianCoffee = CoffeeFactory.createCoffee(CoffeeType.LATTE);
Coffee jackCoffee = CoffeeFactory.createCoffee(CoffeeType.ESPRESSO);
// Coffee mikeCoffee = CoffeeFactory.createCoffee(CoffeeType.TEA);
System.out.println("brianCoffee = " + brianCoffee.getName());
System.out.println("jackCoffee = " + jackCoffee.getName());
// System.out.println("mikeCoffee = " + mikeCoffee.getName()); // IllegalArgumentException
}
}
/*
* brianCoffee = Latte
* jackCoffee = Espresso
*/
- 의존성 주입이라고 할 수 도 있음. CoffeeFactory에서 LatteFacotry의 인스턴스를 생성하는 것이 아닌 주입하기 때문
- 정적 메소드(static으로 설정한 함수)의 경우 클래스를 기반으로 객체를 만들지 않고 호출이 가능하며, 해당 메소드에 대한 메모리 할당을 한번만 할 수 있다는 장점이 있음.
전략 패턴(더 자세한 건 여기로)
객체의 행위를 바꾸고 싶은 경우 코드의 수정없이 캡슐화된 알고리즘을 컨텍스트 안에서 바꿔주면서 상호 교체하는 패턴
import java.util.ArrayList;
import java.util.List;
interface PaymentStrategy {
public void pay(int amount);
}
class KAKAOCardStrategy implements PaymentStrategy {
private String name;
private String cardNumber;
private String cvv;
private String dateOfExpiry;
public KAKAOCardStrategy(String name, String cardNumber, String cvv, String dateOfExpiry) {
this.name = name;
this.cardNumber = cardNumber;
this.cvv = cvv;
this.dateOfExpiry = dateOfExpiry;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using KAKAOCard.");
}
}
class LUNACardStrategy implements PaymentStrategy {
private String emailId;
private String password;
public LUNACardStrategy(String emailId, String password) {
this.emailId = emailId;
this.password = password;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using LUNACard.");
}
}
class Item {
private String name;
private int price;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
class ShoppingCart {
List<Item> items;
public ShoppingCart() {
this.items = new ArrayList<>();
}
public void addItem(Item item) {
this.items.add(item);
}
public void removeItem(Item item) {
this.items.remove(item);
}
public int calculateTotal() {
int sum = 0;
for (Item item : items) {
sum += item.getPrice();
}
return sum;
}
public void pay(PaymentStrategy paymentStrategy) {
int amount = calculateTotal();
paymentStrategy.pay(amount);
}
}
public class StrategyPattern {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
Item A = new Item("hiroA", 100);
Item B = new Item("hiroB", 300);
cart.addItem(A);
cart.addItem(B);
// pay by luna card
cart.pay(new LUNACardStrategy("hiro@example.com", "TESt123!@#"));
// pay by kakao card
cart.pay(new KAKAOCardStrategy("Ipjagum", "123123123", "123", "12/10"));
}
}
/*
* 400 paid using LUNACard.
* 400 paid using KAKAOCard.
*/
옵저버 패턴(더 자세한 건 여기로)
주체가 어떤 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메소드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴
- 주체
- 객체의 상태 변화를 관찰하고 있는 객체
- 옵저버
- 객체의 상태 변화에 따라 전달되는 메소드 등을 기반으로 추가 변화 상항이 생기는 객체
- 이벤트 기반 시스템에서 주로 사용되며 MVC(Model-View-Controller) 패턴에도 사용가능
import java.util.ArrayList;
import java.util.List;
interface Observer {
public void update();
}
interface Subject {
public void register(Observer obj);
public void unregister(Observer obj);
public void notifyObservers();
public String getUpdate(Observer obj);
}
class Topic implements Subject {
private List<Observer> observers;
private String message;
public Topic() {
this.observers = new ArrayList<>();
this.message = "";
}
@Override
public void register(Observer obj) {
if (!observers.contains(obj)) {
observers.add(obj);
}
}
@Override
public void unregister(Observer obj) {
if (observers.contains(obj)) {
observers.remove(obj);
}
}
@Override
public void notifyObservers() {
this.observers.forEach(Observer::update);
}
@Override
public String getUpdate(Observer obj) {
return this.message;
}
public void postMessage(String msg) {
System.out.println("Message sent to Topic: " + msg);
this.message = msg;
notifyObservers();
}
}
class TopicSubscriber implements Observer {
private String name;
private Subject topic;
public TopicSubscriber(String name, Subject topic) {
this.name = name;
this.topic = topic;
}
@Override
public void update() {
String msg = topic.getUpdate(this);
System.out.println(name + ":: got message >> " + msg);
}
}
public class ObserverPattern {
public static void main(String[] args) {
Topic topic = new Topic();
Observer o1 = new TopicSubscriber("A", topic);
Observer o2 = new TopicSubscriber("B", topic);
Observer o3 = new TopicSubscriber("C", topic);
topic.register(o1); topic.register(o2); topic.register(o3);
topic.postMessage("amumu is op champion!");
}
}
/*
Message sent to Topic: amumu is op champion!
A:: got message >> amumu is op champion!
B:: got message >> amumu is op champion!
C:: got message >> amumu is op champion!
*/
- topic은 주체이자 객체
상속과 구현
- 상속
- 자식 클래스가 부모 클래스의 메소드 등을 상속받아 사용하여 자식 클래스에서 추가 및 확장을 할 수 있는 것.
- 재사용성, 중복의 최소화
- 일반 클래스, abstract 클래스를 기반
- 구현
- 부모 인터페이스를 자식 클래스에서 재정의하여 구현하는 것.
- 반드시 부모 클래스의 메소드를 재정의하여 구현
- 인터페이스 기반
프록시 패턴과 프록시 서버(더 자세한 건 여기로)
대상 객체(subject)에 접근하기 전 그 접근에 대한 흐름을 가로채 해당 접근을 필터링 하거나 수정하는 등의 역할을 하는 계층
- 객체의 속성, 변환 등을 보완. 데이터 검증, 캐싱 로깅에 사용.
프록시 서버
서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램을 가리킴
- 프록시 서버로 사용되는 nginx
- nginx는 비동기 이벤트 기반의 구조와 다수의 연결을 효과적으로 처리 가능한 웹서버이며, 주로 node.js 서버 앞단의 프록시 서버로 사용.
이터레이터 페턴(더 자세한 건 여기로)
이터레이터를 사용하여 컬렉션의 요소들에 접근하는 디자인 패턴 이를 통해 순회할 수 있는 여러가지 자료형의 구조와 상관없이 이터레이터라는 인터페이스를 통해 순회 가능.
- 자바에서는 Collection Framework를 상속하는 자료구조에서 사용 가능
MVC 패턴
모델(Model), 뷰(View), 컨트롤러(Controller)로 이루어진 디자인 패턴
애플리케이션의 구성 요소를 세가지로 나누어 개발 프로세스에서 각각의 구성요소에만 집중해서 개발할 수 있도록 만든 디자인 패턴.
- 재사용성과 확장성에 용이
- 모델과 뷰의 관계가 복잡해진다는 단점
- 모델
- 애플리케이션의 데이터인 데이터베이스, 상수, 변수를 의미
- 뷰
- 사용자 인터페이스 요소를 표현.
- 변경이 일어나면 컨트롤러에게 이를 전달해야함.
- 컨트롤러
- 하나 이상의 모델과 하나 이상의 뷰를 잇는 다리 역할.
- 이벤트 등 메인 로직 담당. 모델과 뷰의 생명주기도 관리하며 모델이나 뷰의 변경 통지를 받으면 이를 해석해서 각각의 구성요소에게 해당 내용을 전달.
MVP 패턴
MVC 패턴으로부터 파생. MVC에 해당하는 C가 Presenter로 교체된 패턴
뷰와 프레젠터는 1:1이라 더 강한 결합
MVVM 패턴
C가 View Model로 바뀐 패턴
- 뷰모델은 뷰를 더 추상화한 계층. MVVM 패턴은 MVC 패턴과 다르게 커맨드와 데이터 바인등을 가지는 것이 특징.
- 뷰와 뷰모델 사이의 양방향 데이터 바인딩을 지원하며 UI를 별도의 코드 수정없이 재사용할 수 있고 단위 테스팅 하기 쉽다는 장점.
정리[프로그래밍]
프로그래밍 패러다임
프로개르밍의 관점을 갖게 해주는 역할을 하는 개발 방법론
- 객체 지향 프로그래밍
- 프로그래머들이 프로그램을 상호작용하는 객체들의 집합으로 볼 수 있게 하는 방법론
- 함수형 프로그래밍
- 상태 값을 지니지 않는 함수 값들의 연속으로 생각하는 방법론
- 자바8부터 함수형을 지원하기 위해 람다, 생성자 레퍼런스, 메소드 레퍼런스를 도입하였음.
선언형을 지원하기 위해서는 스트림과 같은 표준 API 도입
프로그래밍 패러다임은 크게 선언형과 명령형으로 나뉨
- 선언형
- 함수형
- 명령형
- 객체 지향형
- 절차 지향형
선언형과 함수형 프로그래밍
무엇을 풀어내는가가 중요한 패러다임
- 순수함수
- 출력이 입력에만 의존하는 코드
- const pure = (a, b) => { return a + b; }
- pure 함수는 a, b에만 영향을 받음
- 고차함수
- 함수가 함수를 값처럼 매개변수로 받아 로직을 생성할 수 있는 것.
- 일급객체
- 변수나 메소드에 변수를 할당할 수 있음.
- 함수 안에 함수를 매개변수로 담을 수 있음.
- 함수가 함수를 반환할 수 있음.
객체 지향 프로그래밍
객체들의 집합으로 프로그램의 상호작용을 표현하며 데이터를 객체로 취급하여 객체 내부에 선언된 메소드를 활용하는 방식
- 설계에 많은 시간이 소요되며 처리속도가 다른 프로그래밍에 비해 느림
- 특징
- 추상화
- 복잡한 시스템으로부터 핵심적인 개념과 기능을 간추려내는 속성
- 캡슐화
- 객체의 속성과 메소드를 하나로 묶고 일부를 외부에 감추어 은닉하는 것
- 상속성
- 상위 클래스의 특성을 하위 클래스가 이어받아 재사용하거나 추가 및 확장하는 속성
- 유지 보수성 증가
- 다형성
- 하나의 메소드나 클래스가 다양한 방법으로 동작하는 것을 의미.
- 오버로딩, 오버라이딩
- 추상화
- 설계 원칙
- SOLID
- 단일 책임 원칙
- 모든 클래스는 하나의 책임만을 가져야하는 원칙
- 개방-폐쇄 원칙
- 확장에는 열려있고 수정에는 닫혀있는 원칙
- 기존 코드는 변경하지 않으면서 추가하는 원칙
- 리스코프 치환 원칙
- 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 하는 것.
- 인터페이스 분리 원칙
- 하나의 일반적인 인터페이스보다 구체적인 여러개의 인터페이스를 만드는 원칙
- 의존성 역전 원칙
- 자신보다 변하기 쉬운것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변하기 쉬운 것의 변화에 영향을 받지 않게 하는 원칙
절차형 프로그래밍
로직이 수행되어야할 연속적인 계산과정
- 코드의 가독성이 좋으면 실행속도가 빠름
- 계산이 많은 작업에 사용(머신러닝 등)
참조
- 면접을 위한 CS 전공지식 노트(주홍철 저) -길벗
'기술면접 > 전산기초' 카테고리의 다른 글
[기술면접] 객체지향 (2) | 2024.11.04 |
---|