SlideShare a Scribd company logo
1 of 39
Download to read offline
C++ API 디자인
Ch.2 품질
아꿈사

Cecil
이 장에서는

좋은 API가 갖춰야 할 기본 품질은?
고품질의 API 설계를 가능하게 하는
특정한 품질 속성이 존재하고,
가능하다면 이 기준을 도입해라
!

설계 품질을 낮추는 많은 요인은 반드시 피해라
고품질 API 설계를 위한 이 책에서 제시하는 기준
•

문제 도메인

•

구체적인 구현 숨기기

•

작게 완성하기

•

쉬운 사용성

•

느슨한 연결

•

안정화와 문서화, 테스트
문제 도메인 모델
API는 문제에 대한 납득할 만한 해결책을 제공해야 한다
훌륭한 추상화 제공
•

API는 논리적인 추상화를 제공해야 함
•

•

저수준의 코드 구현 보다 추상화된 API SET을 제공

But, 훌륭한 추상화 제공은 쉽지 않은 문제
•

일관성 있고 충분한 논리성을 바탕으로 API를 개발해야 함
핵심 객체 모델링
•

API는 문제 도메인의 핵심 객체를 모델링 해야함
•

•

객체 모델링의 주요 목표: 주요 객체 식별 및 연결 관계 확인

API의 핵심 모델은 시간이 지나면 변화가 필요함을 고려해야 함
•

너무 앞서서 필요한 것보다 일반적인 객체 생성은 금물
구체적인 구현 숨기기
클라이언트에 영향을 미치지 않고, 내부 로직을 변경할 수 있도록
구체적인 구현을 숨겨야 한다
물리적 은닉: 선언 vs 정의
•

선언: 컴파일러에서 심벌의 타입과 이름을 알려줌
•

•

정의: 전체적인 세부 사항을 제공
•

•

변수 정의 및 함수의 본문

물리적 은닉
•

•

ex) external int i, class MyClass …

공개된 인터페이스로부터 분리된 파일에 상세한 내부 로직을 구현

가급적이면 API 헤더는 선언만을 제공
•

inline 함수 사용 자제
논리적 은닉: 캡슐화
•

API의 구체적인 로직이 외부로 노출되지 않도록 접근 제한자를 사용

•

C++ 접근 제한자
•

public: 외부에서 접근이 가능
•

구조체의 기본 접근 수준

•

protected: 클래스와 파생된 클래스 내부에서만 접근 가능

•

private: 클래스 내부에서만 접근 가능
•

클래스의 기본 접근 수준
멤버 변수 감추기
•

멤버 변수의 직접 노출보다 getter/setter 메서드를 제공

•

getter/setter의 장점
•
•

지연된 평가

•

캐싱

•

추가 연산

•

알림

•

디버깅

•

동기화

•

훌륭한 접근 제어

•

•

유효성 체크

바뀌지 않는 관계 유지

클래스의 데이터 멤버는 항상 private로 선언
메서드 구현 숨기기
•

public으로 선언할 필요가 없는 메서드를 감추는 것이 중요

•

클래스는 “무엇을 할 것인지를 정의하는 것”

•

C++의 제약 사항
•

클래스를 구현하기 위해 필요한 모든 멤버를 헤더에 선언 해야함

•

해결방안
•

•

Pimple 이디엄 사용 or cpp 파일에 정적 함수를 사용

Tip: private 멤버에 대한 비상수 포인터나 참조를 리턴(X:캡슐화 위반)
클래스 구현 숨기기
•

실제 구현 코드를 가능하면 감춰라.
#include<vector>

!

class Fireworks {
public:
Fireworks();

!
!

!

};

void
void
void
void

SetOrigin(double x, double y);
SetColor(float r, float g, float b);
SetGravity(float g);
SetNumberOfParticles(int num);

void Start();
void Stop();
void Next Frame(float dt);
private:
class FireParticle {
public:
double mX, mY;
double mLifeTime;
};
double mOriginX, mOriginY;
float mRed, mGreen, mBlue;
float mGravity;
float mSpeed;
bool mIsActive;
std::vector<FireParticle *> mParticles;

FireParticle을 외부에 구현하기 보다는
내부 클래스로 구현
작게 완성하기
좋은 API라면 최소한의 크기로 완성되어야 한다
지나친 약속은 금지
•

API의 모든 public 인터페이스는 약속이다

•

새로운 기능 추가는 쉽지만 기존의 것의 변경이 어렵다

•

“더더더” 일반적인 해결책을 찾기 위한 노력을 피해야 하는 이유
•

보다 더한 일반화가 필요한 순간은 오지 않을 수 있다

•

만약 그런날이 온다면, 경험으로 인한 더 좋은 해결책을 내놓을 수 있다

•

추가 기능이 필요하다면, 복잡한 곳보다는 간단한 API에 추가하는 것이
쉽다
가상 함수의 추가는 신중하게
•

상속과 추상화
•

•

의도 했던 것 보다 더 많은 기능들을 노출시킬 수 있는 방법

상속의 함정
•

“깨지기 쉬운 베이스 클래스 문제”: 베이스 클래스의 변경이 클라이언트에 영향을 줌

•

클라이언트는 API 개발자가 의도치 않았던 방법으로 API를 사용할 수 있다

•

클라이언트는 API를 오류가 많이 발생하도록 확장할 수 있다 (동기화 등)

•

클래스 통합을 방해: 기존 함수의 정책에 위반되는 행위를 수행할 경우
C++ 가상 함수 사용시 생각할 점
•

가상 함수 호출은 런타임시 vtable을 탐색

•

가상함수를 사용할 수록 객체의 크기도 비례해서 증가(vtable)

•

가상 함수는 인라인이 될 수 없다

•

가상 함수를 오버로드 하는 것은 어렵다

•

추가적으로 기억할 것
•

소멸자는 항상 가상 함수로 선언

•

메소드 호출 관계를 문서화

•

생성자나 호출자에서는 절대 가상함수를 호출하지 않음
편리한 API
•

기능에 초점을 맞춘 순수 API 제공 vs. 편리한 래퍼 API 제공

•

순수 API 제공
•

•

래퍼 API 제공
•

•

경량화되고 기능에 집중, 구현 코드의 복잡성을 줄임

클라이언트는 적은 양의 코드를 통해서 기본적인 기능이 동작

2.4 Easy to use
39
최소화시킨 핵심 API를 기반으로 분리된 모듈이나 라이브러리를 통해서 사용하기 편리한 API를 제공

FIGURE 2.4
An example of a core API (OpenGL) separated from convenience APIs layered on top of it (GLU and GLUT).

GLUquadric *qobj

gluNewQuadric();
쉬운 사용성
잘 설계한 API라면 간단한 작업을 쉽고 명확하게 만들어야 한다
한눈에 들어오는
•

사용자가 API를 어떻게 사용해야 할지 한눈에 이해

•

클래스와 함수 이름을 잘 선택해서 직관적이고 논리적인 객체를
모델
•

자세한 내용은 4장에서
names in Chapter 4 when I discuss API design techniques. Avoiding the use of abbreviations can also
play a factor in discoverability (Blanchette, 2008) so that users don’t have to remember if your API
uses GetCurrentValue(), GetCurrValue(), GetCurValue(), or GetCurVal().

2.4.2 Difficult to Misuse

A good 사용하기에도 use, should also be difficult
being easy to 어렵게
Scott
잘못API, inis addition toimportant general interface design guideline to misuse.2004). Meyersofsuggests that this the most
(Meyers,
Some the
most common ways to misuse an API include passing the wrong arguments to a method or passing
illegal values to a method. These can happen when you have multiple arguments of the same type
and the user forgets the correct order of the arguments or where you use an int to represent a small
•range of values instead of a more constrained enum type (Bloch, 2008). For example, consider the
following method signature:

좋은 API라면 잘못 사용하기에도 어려워야 함

std::string FindString(const std::string &text,
bool search forward,
<boolean 사용>
bool case sensitive);

2.4 Easy to use

41

!

It would be easy for users to forget whether the first bool argument is the search direction or the
caseenum SearchDirection { the flags in the wrong<enum 사용>
sensitivity flag. Passing
order would result in unexpected behavior and
FORWARD,
probably!cause the user to waste a few minutes debugging the problem, until they realized that they
BACKWARD
had };
transposed the bool arguments. However, you could design the method so that the compiler
catches this kind of error for them by introducing a new enum type for each option. For example,
enum CaseSensitivity {

!

CASE SENSITIVE
CASE INSENSITIVE

};
std::string FindString(const std::string &text,
SearchDirection direction,
CaseSensitivity case sensitivity);

!
•

Not only does this mean that users cannot mix up the order of the two flags, because it would generate a compilation error, but also the code they have to write is now more self-descriptive. Compare

코드의 가독성을true, false); 위해서 Boolean보다는 enum을 사용
높이기
result FindString(text,

with

•

Tip: 함수에 같은 타입의 파라미터를 여러개 사용하지 말자
result

FindString(text, FORWARD, CASE INSENSITIVE);

TIP
Prefer enums to booleans to improve code readability.
abbreviations in several of its method names, such as prevValue() and previousSibling(). This is
another example of why the use of abbreviations should be avoided at all costs. use
2.4 Easy to
43
The use of consistent method signatures is an equally critical design quality. If you have several
methods that accept similar argument lists, you should endeavor to keep a consistent number and
order for those arguments. To give a counterexample, I refer you to the following functions from
2.4.3 Consistent
the standard C library:

일관성 있는

A good API should apply a consistent design approach so that its conventions are easy to remember,
and void bcopy(const void *s1, void 2008).size applies to all aspects of API design, such as
therefore easy to adopt (Blanchette, *s2, This t n);
char *strncpy(char *restrict s1, const char *restrict s2, size t n);
• naming conventions, parameter order, the use of standard patterns, memory model semantics, the
use Both of these error handling, and so on.
of exceptions, functions involve copying n bytes of data from one area of memory to another.
In terms of bcopy() these, consistent data from s1 into imply reuse strncpy() copies the
However, the the first offunction copies naming conventions s2, whereasof the same words forfrom s2 into
same concepts across the API. For example, if you have decided to use the verb pairs Begin and End,
s1.•This can give rise to subtle memory errors if a developer were to decide to switch usage between
you should not mingle the terms Start and Finish. As another example, the Qt3 API mixes the use of
the two functions without a methodreading such as respective man pages. To be sure, there is a clue to
close names, of the prevValue() and previousSibling(). This is
abbreviations in several of its
the conflicting specifications in the function signatures: note the usecosts. const pointer in each case.
another example of why the use of abbreviations should be avoided at all of the
However, thisconsistent method signatures is won’t be caught by a compiler if the source pointer is not
The use of could be missed easily and an equally critical design quality. If you have several
declared thatbe const.
methods to accept similar argument lists, you should endeavor to keep a consistent number and
• order for also the inconsistent useaof the words “copy” and “cpy.” following functions from
Note those arguments. To give counterexample, I refer you to the
the Let’s take another example from the standard C library. The familiar malloc() function is used to
standard C library:

API 설계 관점에서의 일관성

명명 규칙, 파라미터의 순서, 표준 패턴의 사용, 의미론적인 메
모리 모델, 예외 및 오류 처리 등.

일관성을 지키지 못한 사례

allocate bcopy(const void *s1, void *s2, and then);
void a contiguous block of memory, size t calloc() function performs the same operation with
the!char *strncpy(char *restrict s1, const char *restrict zerosize t n);
addition that it initializes the reserved memory with s2, bytes. However, despite their similar
purpose, they have different function signatures:

Both of these functions involve copying n bytes of data from one area of memory to another.
However,*calloc(size t count, size t size); s2, whereas strncpy() copies from s2 into
void the bcopy() function copies data from s1 into
s1.!This can give rise to subtle memory errors if a developer were to decide to switch usage between
void *malloc(size t size);
the two functions without a close reading of the respective man pages. To be sure, there is a clue to
Theconflicting specifications in the a size insignatures:bytes, whereasthe const pointer in eachcount * size)
the malloc() function accepts function terms of note the use of calloc() allocates ( case.
bytes. In this could be missed inconsistent, this violatesby a compiler if the least surprise.isAs a further
However, addition to being easily and won’t be caught the principle of source pointer not
•example,tothe const. and write() standard C functions accept a file descriptor as their first paradeclared be read()
Note also the the fgets() and fputs() “copy” and “cpy.”
meter, whereas inconsistent use of the words functions require the file descriptor to be specified last
Let’s take another
(Henning, 2009). example from the standard C library. The familiar malloc() function is used to

Tip: 함수의 이름, 파라미터의 순서를 일관성 있게 유지하라
44

CHAPTER 2 Qualities

클래스 수준의 일관성
•

•
•
•

The STL is a great example of this. The std::vector, std::set, std::map, and
classes all offer a size() method to return the number of elements in the cont
also all support the use of iterators, once you know how to iterate through a
apply the same knowledge to a std::map. This makes it easier to memorize the pr
비슷한 기능을 제공하는 클래스들은 비슷한 인터페이스를 of the API. this kind of consistency for free through polymorphism: by placin
제공 get
You
tionality into a common base class. However, often it doesn’t make sense for
inherit from a common base class, and you shouldn’t introduce a base class pure
• ex) STL, std::vector, std::set, std::map의 size 함수 it increases the complexity and class count for your interface. Indeed, it’s n
as
STL container classes do not inherit from a common base class. Instead, yo
design for this by manually identifying the common concepts across your cla
다형성을 적용하면 일관성 얻기가 용이
same conventions to represent these concepts in each class. This is often re
polymorphism.
You can also make use of C++ templates to help you define and apply this k
For example, you could create a template 표현: coordinate class
공통 베이스 클래스를 둘 수 없는 경우에도 각 클래스에 공통된 개념들을 같은 이름으로 for a 2D 정적 다형성and specia
floats, and doubles. In this way you are assured that each type of coordinate offe
interface. The following code sample offers a simple example of this:

C++ 템플릿을 사용한 일관성 유지
•

ex) Coord2D<int>, Coord2D<float> ..

template <typename T>
class Coord2D
{
public:
Coord2D(T x, T y) : mX(x), mY(y) {};
T GetX() const { return mX; }
T GetY() const { return mY; }
void SetX(T x) { mX
void SetY(T y) { mY

x; }
y; }

void Add(T dx, T dy) { mX þ dx; mY þ dy; }
void Multiply(T dx, T dy) { mX * dx; mY * dy; }
private:
T mX;
T mY;
};

With this template definition, you can create variables of type Coord2D<int>, Coo
Coord2D<double> and all of these will have exactly the same interface.
수직적인
•

다른 코드에 영향을 미치지 않는 함수
•

•

ex) 속성 값을 할당하는 메서드 호출은 그 속성 값만 변화

수직적 API 설계시 고려할 사항
•

중복 제거: 같은 정보가 2가지 이상의 방법으로 반복되지 않게 함

•

독립성 증가: 모든 중첩되는 개념들은 각각의 기반 컨포넌트로 분리
float CheapMotelShower::GetTemperature() const {
return mTemperature;
}

!

float CheapMotelShower::GetPower() const {
return mPower;
}

!

void CheapMotelShower::SetPower(float p) {
if (p < 0) p = 0;
if (p > 100) p = 100;
mPower = p;
mTemperature = 42.0f þ sin(p/38.0f) * 45.0f;
}

float IdealShower::GetTemperature() const {
return mTemperature;
}

!

float IdealShower::GetPower() const {
return mPower;
}

!

void IdealShower::SetTemperature(float t) {
if (t < 42) t = 42;
if (t > 85) t = 85;
mTemperature = t;
}
void IdealShower::SetPower(float p) {
if (p < 0) p = 0;
if (p > 100) p = 100;
mPower = p;
}
견고한 자원 할당
•

C++의 메모리 관련 이슈
•
•

메모리 이중 해제

•

할당자 혼용

•

잘못된 배열 해제

•

•

Null 역참조

메모리 누수

관리되는 포인터를 사용
•

공유 포인터: boost::shared_ptr, …

•

약한 포인터: boost::weak_ptr, …

•

범위 한정 포인터: boost::scoped_ptr, …
플랫폼 독립적
•

bool StartCall(const std::string &number);
bool EndCall();
#if defined TARGET OS IPHONE
bool GetGPSLocation(double &lat, double &lon);
#endif
};

This poor design creates a different API on different platforms. Doing so
your API to introduce the same platform specificity into their own applicatio
2.4 Easy to use
51
the aforementioned case, your clients would have to guard any calls to GetGPSL
cisely the same #if conditional statement, otherwise their code may fail to co
fined symbol error on other platforms.
Furthermore, if in a later version of the API you also add support for anot
2.4.6 Platform Independent
Windows Mobile, then you would have to update the #if line in your publ
A well-designed C++ API should always avoid platform-specific #if/#ifdef lines in its public. head- your clients would have to find all instances in their co
WIN32 WCE Then,
ers. If your API presents a high-level and logical model for your problem domain,embedded the TARGET OS IPHONE define and extend it to also include WIN32
as it should, there
are very few cases where the API should be different for different platforms. About the only cases
you have unwittingly exposed the implementation details of your API.
where this may be appropriate are when you are writing an API to interface with a platform-specific
Instead, you should hide the fact that the function only works on certain pla
resource, such as a routine that draws in a window and requires the appropriate window handle to be
method to determine whether the implementation offers the desired capabilitie
passed in for the native operating system. Barring these kinds of situations, you shouldFor example,
form. never write
public header files with platform-specific #ifdef lines.
class MobilePhone
For example, let’s take the case of an API that encapsulates the functionality offered by a mobile
phone. Some mobile phones offer built-in GPS devices that can deliver the geographic location of 2 Qualities
52{
CHAPTER
public:
the phone, but not all devices offer this capability. However, you should never expose this situation
bool StartCall(const std::string &number);
directly through your API, such as in the following example:

잘 설계된 API라면 특정 플랫폼에 독립적인 #if/#ifdef 코드를
public 헤더에 사용하지 않아야 함

class MobilePhone
{
public:
bool StartCall(const std::string &number);
bool EndCall();
#if defined TARGET OS IPHONE
bool GetGPSLocation(double &lat, double &lon);
#endif
};

bool EndCall();
bool HasGPS() const;
Now your API is consistent over all platforms and does not expose the details of wh
bool GetGPSLocation(double &lat, double &lon);
port GPS coordinates. The client can now write code to check whether the current
};
GPS device, by calling HasGPS(), and if so they can call the GetGPSLocation()
the actual coordinate. The implementation of the HasGPS() method might look som

bool MobilePhone::HasGPS() const
{
#if defined TARGET OS IPHONE
return true;
#else
This poor design creates a different API on different platforms. Doing so forces the clients of
return false;
your API to introduce the same platform specificity into their own applications. For example, in
#endif
the aforementioned case, your clients would have to guard any calls to GetGPSLocation() with pre}
cisely the same #if conditional statement, otherwise their code may fail to compile with an unde-

This is far superior to the original design because the platform-specific #if statem
fined symbol error on other platforms.
in the .cpp file instead of being exposed in the header file.
Furthermore, if in a later version of the API you also add support for another device class, say
Windows Mobile, then you would have to update the #if line in your public header to include
TIP
WIN32 WCE. Then, your clients would have to find all instances in their code where they have
This is platform
embedded the TARGET OS IPHONE define and extend it to also include WIN32 WCE.Never put because specific #if or #ifdef statements into your public APIs. It exposes impleme
you have unwittingly exposed the implementation details of your API.
makes your API appear different on different platforms.
Instead, you should hide the fact that the function only works on certain platforms and provide a
느슨한 연결
좋은 API는 느슨한 연결과 높은 결합성을 보인다
이름만을 사용한 연결
•

클래스 전체 선언을 참조할 필요가 없다면 전방 선언을 사용
class MyObject; // only need to know the name of MyObject

!

class MyObjectHolder {
public:
MyObjectHolder();
void SetObject(MyObject *obj); MyObject *GetObject() const;
private:
MyObject *mObj;
};
클래스 연결 줄이기
•

연결 관계를 줄이기 위해 멤버 함수 대신 비멤버, 비프렌드 함수
사용
// myobject.h
class MyObject {
public:
void PrintName() const;
std::string GetName() const;
...
protected:
...
private:
std::string mName; ...
};

// myobject.h
class MyObject {
public:
std::string GetName() const;
...
protected:
...
private:
std::string mName; ...
};

!

void PrintName(const MyObject &obj);
의도적인 중복
•

심각한 연결 관계를 잘라내기 위해 적은 양의 중복코드를 추가하
는 것이 효과적일 경우
#include "ChatUser.h"
#include <string>
#include <vector>
class TextChatLog {
public:
bool AddMessage(const ChatUser &user,
const std::string &msg);
int GetCount() const;
std::string GetMessage(int index);
private:
struct ChatEvent {
ChatUser mUser;
std::string mMessage;
size t mTimestamp;
};
std::vector<ChatEvent> mChatEvents;
};

#include <string>
#include <vector>
class TextChatLog {
public:
bool AddMessage(const std::string &user,
const std::string &msg);
int GetCount() const;
std::string GetMessage(int index);
private:
struct ChatEvent {
std::string mUserName;
std::string mMessage;
size t mTimestamp;
};
std::vector<ChatEvent> mChatEvents;
};

ChatUser에 대한 연결 관계가 없어짐
58

CHAPTER 2 Qualities

2.5.4 Manager Classes
A manager class is one that owns and coordinates several lower-level classes. This can be used to
break the dependency of one or more classes upon a collection of low-level classes. For example,
consider a structured drawing program that lets you create 2D objects, select objects, and move them
around a canvas. The program supports several kinds of input devices to let users select and move
objects, such as a mouse, tablet, and joystick. A naive design would require both select and move
operations to know about each kind of input device, as shown in the UML diagram (Figure 2.5).
Alternatively, you could introduce a manager class to coordinate access to each of the specific
input device classes. In this way, the SelectObject and MoveObject classes only need to depend
on this single manager class, and then only the manager class needs to depend on the individual input
device classes. This may also require creating some form of abstraction for the underlying classes.
For example, note that MouseInput, TabletInput, and JoystickInput each have a slightly different
interface. Our manager class could therefore put in place a generic input device interface that
abstracts away the specifics of a particular device. The improved, more loosely coupled, design is
shown in Figure 2.6.
Note that this design also scales well too. This is because more input devices can be added to the
system without introducing any further dependencies for SelectObject or MoveObject. Also, if you
decided to add additional manipulation objects, such as RotateObject and ScaleObject, they only
need a single dependency on InputManager instead of each introducing further coupling to the
underlying device classes.

매니저 클래스
•

하위 수준에 여러개의 클래스를 포함하면서 중재하는 역할을 수행

•

여러개의 클래스들을 대상으로 하나 혹의 그 이상의 의존성을 줄임
2.5 Loosely coupled

MoveObject

SelectObject

+ DoMove() : void

+ DoSelect() : void

MoveObject

SelectObject

+ DoMove() : void

+ DoSelect() : void

InputManager
+ GetXCoord() : integer
+ GetYCoord() : integer
+ IsButton1Down() : boolean
+ IsButton2Down() : boolean
+ IsButton3Down() : boolean

MouseInput
+ GetXCoord() : integer
+ GetYCoord() : integer
+ IsLMBDown() : boolean
+ IsMMBDown() : boolean
+ IsRMBDown() : boolean

TabletInput
+ GetXCoord() : integer
+ GetYCoord() : integer
+ IsPenDown() : boolean

JoystickInput
+ GetXCoord() : integer
+ GetYCoord() : integer
+ IsTriggerDown() : boolean

MouseInput
+ GetXCoord() : integer
+ GetYCoord() : integer
+ IsLMBDown() : boolean
+ IsMMBDown() : boolean
+ IsRMBDown() : boolean

FIGURE 2.5

TabletInput
+ GetXCoord() : integer
+ GetYCoord() : integer
+ IsPenDown() : boolean

Multiple high-level classes each coupled to several low-level classes.
FIGURE 2.6
Using a manager class to reduce coupling to lower-level classes.

JoystickInput
+ GetXCoord() : integer
+ GetYCoord() : integer
+ IsTriggerDown() : boolean

59
콜백과 옵저버, 알림
•

이벤트가 발생했을때 이를 다른 클래스에 알리는 API

•

일반적인 이슈
•

재진입성
•

•

수명 관리
•

•

알림을 받은 코드가 다시 API를 호출하는 경우를 고려

이벤트에 대한 구독/해지 기능 제공, 또한 중복 이벤트를 받지 않도록 고려

이벤트 순서
•

이벤트의 순서를 명확히 정의 (네이밍 등의 방법으로 명시)
콜백
•

저수준 코드가 고수준의 코드를 사용할때 의존성을 없애는 방법

•

대규모 프로젝트에서 순환 의존 고리를 제거
!
!
!
!

•

#include <string>
class ModuleB {
public:
typedef void (*CallbackType)(const std::string &name, void *data);
void SetCallback(CallbackType cb, void *data);
...
private:
CallbackType mCallback; void *mClosure;
};

!

if (mCallback) {
(*mCallback)("Hello World", mClosure);
}

객체지향 프로그램에서 인스턴스 메소드를 콜백으로 사용하는 경우 this 객
체 포인터를 전달해야 함
옵저버
•

콜백은 객체지향에서는 좋은 해결책이 되지 못함
•

•

this 이슈

좀 더 객체지향적인 해결책 옵저버
•

3장과 4장에서 …
알림
•

콜백과 옵저버는 특정 작업을 위해 생성

•

알림
•

시스템에서 연결되지 않은 부분 사이에서 알림이나 이벤트를 보내는 메커니즘을 중앙화

•

보내는 이와 받는 이 사이의 의존성이 전혀 없음

•

ex) signal/slot
class MySlot {
public:
void operator()() const {
std::cout << "MySlot called!" << std::endl; }
};
// Create an instance of our MySlot class
MySlot slot;

!

// Create a signal with no arguments and a void return value
boost::signal<void ()> signal;

!

// Connect our slot to this signal
signal.connect(slot);

!

// Emit the signal and thereby call all of the slots
signal();
안정화와 문서화, 테스트
!
좋은 API라면
- 안정적이어야 하고, 미래 증명적이어야 함.
- 명확히 이해할 수 있게끔 구체적인 문서를 제공해야 함
- 변경이 발생하더라도 기존 기능에 영향이 가지 않도록 자동화된 테스트 프로세스를 갖추어야 함
결론
가능하다면 고품질 API 설계를 가능케하는 기준들을 도입하고,
품질을 낮추는 많은 요인들은 반드시 피해라
But, 아무리 잘 정리된 규칙도 모든 상황에 적용 불가
각 프로젝트 특성을 고려한 다음 가장 적합한 해결책을 적용
References
• Martin Reddy (2014). C++ API 디자인. (천호민, 옮김). 고양시: 지앤선. (원서출판
2013)

More Related Content

Viewers also liked

Code 11 논리 게이트
Code 11 논리 게이트Code 11 논리 게이트
Code 11 논리 게이트HyeonSeok Choi
 
서버인프라를지탱하는기술3_2_3
서버인프라를지탱하는기술3_2_3서버인프라를지탱하는기술3_2_3
서버인프라를지탱하는기술3_2_3HyeonSeok Choi
 
Refactoring 메소드 호출의 단순화
Refactoring 메소드 호출의 단순화Refactoring 메소드 호출의 단순화
Refactoring 메소드 호출의 단순화HyeonSeok Choi
 
MiningTheSocialWeb.Ch2.Microformat
MiningTheSocialWeb.Ch2.MicroformatMiningTheSocialWeb.Ch2.Microformat
MiningTheSocialWeb.Ch2.MicroformatHyeonSeok Choi
 
SICP_2.5 일반화된 연산시스템
SICP_2.5 일반화된 연산시스템SICP_2.5 일반화된 연산시스템
SICP_2.5 일반화된 연산시스템HyeonSeok Choi
 
CODE Ch.21 버스에 올라 탑시다
CODE Ch.21 버스에 올라 탑시다CODE Ch.21 버스에 올라 탑시다
CODE Ch.21 버스에 올라 탑시다HyeonSeok Choi
 
자바 병렬 프로그래밍 1&2
자바 병렬 프로그래밍 1&2자바 병렬 프로그래밍 1&2
자바 병렬 프로그래밍 1&2HyeonSeok Choi
 
컴퓨터 프로그램 구조와 해석 3.5
컴퓨터 프로그램 구조와 해석 3.5컴퓨터 프로그램 구조와 해석 3.5
컴퓨터 프로그램 구조와 해석 3.5HyeonSeok Choi
 
서버인프라를지탱하는기술2_1-2
서버인프라를지탱하는기술2_1-2서버인프라를지탱하는기술2_1-2
서버인프라를지탱하는기술2_1-2HyeonSeok Choi
 
실무로 배우는 시스템 성능 최적화 Ch6
실무로 배우는 시스템 성능 최적화 Ch6실무로 배우는 시스템 성능 최적화 Ch6
실무로 배우는 시스템 성능 최적화 Ch6HyeonSeok Choi
 
실무로 배우는 시스템 성능 최적화 Ch7
실무로 배우는 시스템 성능 최적화 Ch7실무로 배우는 시스템 성능 최적화 Ch7
실무로 배우는 시스템 성능 최적화 Ch7HyeonSeok Choi
 
HTTP 완벽가이드 16장
HTTP 완벽가이드 16장HTTP 완벽가이드 16장
HTTP 완벽가이드 16장HyeonSeok Choi
 
Mining the social web ch1
Mining the social web ch1Mining the social web ch1
Mining the social web ch1HyeonSeok Choi
 
Abstract factory petterns
Abstract factory petternsAbstract factory petterns
Abstract factory petternsHyeonSeok Choi
 
Head first statistics14
Head first statistics14Head first statistics14
Head first statistics14HyeonSeok Choi
 
C++ API 디자인 - 확장성
C++ API 디자인 - 확장성C++ API 디자인 - 확장성
C++ API 디자인 - 확장성HyeonSeok Choi
 
실무로 배우는 시스템 성능 최적화 10부. 네트워크 모니터링
실무로 배우는 시스템 성능 최적화   10부. 네트워크 모니터링실무로 배우는 시스템 성능 최적화   10부. 네트워크 모니터링
실무로 배우는 시스템 성능 최적화 10부. 네트워크 모니터링Hyunsoo Jung
 

Viewers also liked (20)

Code 11 논리 게이트
Code 11 논리 게이트Code 11 논리 게이트
Code 11 논리 게이트
 
서버인프라를지탱하는기술3_2_3
서버인프라를지탱하는기술3_2_3서버인프라를지탱하는기술3_2_3
서버인프라를지탱하는기술3_2_3
 
Refactoring 메소드 호출의 단순화
Refactoring 메소드 호출의 단순화Refactoring 메소드 호출의 단순화
Refactoring 메소드 호출의 단순화
 
MiningTheSocialWeb.Ch2.Microformat
MiningTheSocialWeb.Ch2.MicroformatMiningTheSocialWeb.Ch2.Microformat
MiningTheSocialWeb.Ch2.Microformat
 
SICP_2.5 일반화된 연산시스템
SICP_2.5 일반화된 연산시스템SICP_2.5 일반화된 연산시스템
SICP_2.5 일반화된 연산시스템
 
CODE Ch.21 버스에 올라 탑시다
CODE Ch.21 버스에 올라 탑시다CODE Ch.21 버스에 올라 탑시다
CODE Ch.21 버스에 올라 탑시다
 
자바 병렬 프로그래밍 1&2
자바 병렬 프로그래밍 1&2자바 병렬 프로그래밍 1&2
자바 병렬 프로그래밍 1&2
 
컴퓨터 프로그램 구조와 해석 3.5
컴퓨터 프로그램 구조와 해석 3.5컴퓨터 프로그램 구조와 해석 3.5
컴퓨터 프로그램 구조와 해석 3.5
 
HTTPS
HTTPSHTTPS
HTTPS
 
서버인프라를지탱하는기술2_1-2
서버인프라를지탱하는기술2_1-2서버인프라를지탱하는기술2_1-2
서버인프라를지탱하는기술2_1-2
 
실무로 배우는 시스템 성능 최적화 Ch6
실무로 배우는 시스템 성능 최적화 Ch6실무로 배우는 시스템 성능 최적화 Ch6
실무로 배우는 시스템 성능 최적화 Ch6
 
실무로 배우는 시스템 성능 최적화 Ch7
실무로 배우는 시스템 성능 최적화 Ch7실무로 배우는 시스템 성능 최적화 Ch7
실무로 배우는 시스템 성능 최적화 Ch7
 
HTTP 완벽가이드 16장
HTTP 완벽가이드 16장HTTP 완벽가이드 16장
HTTP 완벽가이드 16장
 
MutiCore 19-20
MutiCore 19-20MutiCore 19-20
MutiCore 19-20
 
Mining the social web ch1
Mining the social web ch1Mining the social web ch1
Mining the social web ch1
 
Abstract factory petterns
Abstract factory petternsAbstract factory petterns
Abstract factory petterns
 
Head first statistics14
Head first statistics14Head first statistics14
Head first statistics14
 
C++ API 디자인 - 확장성
C++ API 디자인 - 확장성C++ API 디자인 - 확장성
C++ API 디자인 - 확장성
 
실무로 배우는 시스템 성능 최적화 10부. 네트워크 모니터링
실무로 배우는 시스템 성능 최적화   10부. 네트워크 모니터링실무로 배우는 시스템 성능 최적화   10부. 네트워크 모니터링
실무로 배우는 시스템 성능 최적화 10부. 네트워크 모니터링
 
Hadoop overview
Hadoop overviewHadoop overview
Hadoop overview
 

Similar to C++ api design 품질

카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.Ryan Park
 
About Visual C++ 10
About  Visual C++ 10About  Visual C++ 10
About Visual C++ 10흥배 최
 
The art of readable code ch4 ch8
The art of readable code ch4   ch8The art of readable code ch4   ch8
The art of readable code ch4 ch8Ki Sung Bae
 
Rails style-guide-2
Rails style-guide-2Rails style-guide-2
Rails style-guide-2Yunho Jo
 
읽기 좋은 코드가 좋은코드다
읽기 좋은 코드가 좋은코드다읽기 좋은 코드가 좋은코드다
읽기 좋은 코드가 좋은코드다wonmin lee
 
Programming skills 1부
Programming skills 1부Programming skills 1부
Programming skills 1부JiHyung Lee
 
Chapter7~9 ppt
Chapter7~9 pptChapter7~9 ppt
Chapter7~9 pptInjae Lee
 
[스프링 스터디 2일차] AOP
[스프링 스터디 2일차] AOP[스프링 스터디 2일차] AOP
[스프링 스터디 2일차] AOPAnselmKim
 
Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4성연 김
 
C Language II
C Language IIC Language II
C Language IISuho Kwon
 
어플리케이션 성능 최적화 기법
어플리케이션 성능 최적화 기법어플리케이션 성능 최적화 기법
어플리케이션 성능 최적화 기법Daniel Kim
 
Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Nam Hyeonuk
 
A tour of C++ : the basics
A tour of C++ : the basicsA tour of C++ : the basics
A tour of C++ : the basicsJaewon Choi
 
Refactoring tutorial
Refactoring tutorialRefactoring tutorial
Refactoring tutorialBingu Shim
 
Architecture patterns with python (2)
Architecture patterns with python (2)Architecture patterns with python (2)
Architecture patterns with python (2)동환 김
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinDong Chan Shin
 
타입스크립트 잘 사용하기
타입스크립트 잘 사용하기타입스크립트 잘 사용하기
타입스크립트 잘 사용하기SanghoYun
 
깨끗한 코드 (클린 코드, Clean Code)
깨끗한 코드 (클린 코드, Clean Code)깨끗한 코드 (클린 코드, Clean Code)
깨끗한 코드 (클린 코드, Clean Code)Jay Park
 
Effective C++ Chapter 1 Summary
Effective C++ Chapter 1 SummaryEffective C++ Chapter 1 Summary
Effective C++ Chapter 1 SummarySeungYeonChoi10
 

Similar to C++ api design 품질 (20)

카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
 
About Visual C++ 10
About  Visual C++ 10About  Visual C++ 10
About Visual C++ 10
 
7 8 1
7 8 17 8 1
7 8 1
 
The art of readable code ch4 ch8
The art of readable code ch4   ch8The art of readable code ch4   ch8
The art of readable code ch4 ch8
 
Rails style-guide-2
Rails style-guide-2Rails style-guide-2
Rails style-guide-2
 
읽기 좋은 코드가 좋은코드다
읽기 좋은 코드가 좋은코드다읽기 좋은 코드가 좋은코드다
읽기 좋은 코드가 좋은코드다
 
Programming skills 1부
Programming skills 1부Programming skills 1부
Programming skills 1부
 
Chapter7~9 ppt
Chapter7~9 pptChapter7~9 ppt
Chapter7~9 ppt
 
[스프링 스터디 2일차] AOP
[스프링 스터디 2일차] AOP[스프링 스터디 2일차] AOP
[스프링 스터디 2일차] AOP
 
Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4
 
C Language II
C Language IIC Language II
C Language II
 
어플리케이션 성능 최적화 기법
어플리케이션 성능 최적화 기법어플리케이션 성능 최적화 기법
어플리케이션 성능 최적화 기법
 
Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약
 
A tour of C++ : the basics
A tour of C++ : the basicsA tour of C++ : the basics
A tour of C++ : the basics
 
Refactoring tutorial
Refactoring tutorialRefactoring tutorial
Refactoring tutorial
 
Architecture patterns with python (2)
Architecture patterns with python (2)Architecture patterns with python (2)
Architecture patterns with python (2)
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshin
 
타입스크립트 잘 사용하기
타입스크립트 잘 사용하기타입스크립트 잘 사용하기
타입스크립트 잘 사용하기
 
깨끗한 코드 (클린 코드, Clean Code)
깨끗한 코드 (클린 코드, Clean Code)깨끗한 코드 (클린 코드, Clean Code)
깨끗한 코드 (클린 코드, Clean Code)
 
Effective C++ Chapter 1 Summary
Effective C++ Chapter 1 SummaryEffective C++ Chapter 1 Summary
Effective C++ Chapter 1 Summary
 

More from HyeonSeok Choi

밑바닥부터시작하는딥러닝 Ch05
밑바닥부터시작하는딥러닝 Ch05밑바닥부터시작하는딥러닝 Ch05
밑바닥부터시작하는딥러닝 Ch05HyeonSeok Choi
 
밑바닥부터시작하는딥러닝 Ch2
밑바닥부터시작하는딥러닝 Ch2밑바닥부터시작하는딥러닝 Ch2
밑바닥부터시작하는딥러닝 Ch2HyeonSeok Choi
 
프로그래머를위한선형대수학1.2
프로그래머를위한선형대수학1.2프로그래머를위한선형대수학1.2
프로그래머를위한선형대수학1.2HyeonSeok Choi
 
알고리즘 중심의 머신러닝 가이드 Ch04
알고리즘 중심의 머신러닝 가이드 Ch04알고리즘 중심의 머신러닝 가이드 Ch04
알고리즘 중심의 머신러닝 가이드 Ch04HyeonSeok Choi
 
딥러닝 제대로시작하기 Ch04
딥러닝 제대로시작하기 Ch04딥러닝 제대로시작하기 Ch04
딥러닝 제대로시작하기 Ch04HyeonSeok Choi
 
밑바닥부터시작하는딥러닝 Ch05
밑바닥부터시작하는딥러닝 Ch05밑바닥부터시작하는딥러닝 Ch05
밑바닥부터시작하는딥러닝 Ch05HyeonSeok Choi
 
7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성HyeonSeok Choi
 
7가지 동시성 모델 4장
7가지 동시성 모델 4장7가지 동시성 모델 4장
7가지 동시성 모델 4장HyeonSeok Choi
 
실무로 배우는 시스템 성능 최적화 Ch8
실무로 배우는 시스템 성능 최적화 Ch8실무로 배우는 시스템 성능 최적화 Ch8
실무로 배우는 시스템 성능 최적화 Ch8HyeonSeok Choi
 
Logstash, ElasticSearch, Kibana
Logstash, ElasticSearch, KibanaLogstash, ElasticSearch, Kibana
Logstash, ElasticSearch, KibanaHyeonSeok Choi
 
실무로배우는시스템성능최적화 Ch1
실무로배우는시스템성능최적화 Ch1실무로배우는시스템성능최적화 Ch1
실무로배우는시스템성능최적화 Ch1HyeonSeok Choi
 
HTTP 완벽가이드 21장
HTTP 완벽가이드 21장HTTP 완벽가이드 21장
HTTP 완벽가이드 21장HyeonSeok Choi
 
HTTP 완벽가이드 6장.
HTTP 완벽가이드 6장.HTTP 완벽가이드 6장.
HTTP 완벽가이드 6장.HyeonSeok Choi
 
HTTP 완벽가이드 1장.
HTTP 완벽가이드 1장.HTTP 완벽가이드 1장.
HTTP 완벽가이드 1장.HyeonSeok Choi
 

More from HyeonSeok Choi (20)

밑바닥부터시작하는딥러닝 Ch05
밑바닥부터시작하는딥러닝 Ch05밑바닥부터시작하는딥러닝 Ch05
밑바닥부터시작하는딥러닝 Ch05
 
밑바닥부터시작하는딥러닝 Ch2
밑바닥부터시작하는딥러닝 Ch2밑바닥부터시작하는딥러닝 Ch2
밑바닥부터시작하는딥러닝 Ch2
 
프로그래머를위한선형대수학1.2
프로그래머를위한선형대수학1.2프로그래머를위한선형대수학1.2
프로그래머를위한선형대수학1.2
 
알고리즘 중심의 머신러닝 가이드 Ch04
알고리즘 중심의 머신러닝 가이드 Ch04알고리즘 중심의 머신러닝 가이드 Ch04
알고리즘 중심의 머신러닝 가이드 Ch04
 
딥러닝 제대로시작하기 Ch04
딥러닝 제대로시작하기 Ch04딥러닝 제대로시작하기 Ch04
딥러닝 제대로시작하기 Ch04
 
밑바닥부터시작하는딥러닝 Ch05
밑바닥부터시작하는딥러닝 Ch05밑바닥부터시작하는딥러닝 Ch05
밑바닥부터시작하는딥러닝 Ch05
 
함수적 사고 2장
함수적 사고 2장함수적 사고 2장
함수적 사고 2장
 
7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성
 
7가지 동시성 모델 4장
7가지 동시성 모델 4장7가지 동시성 모델 4장
7가지 동시성 모델 4장
 
Bounded Context
Bounded ContextBounded Context
Bounded Context
 
DDD Repository
DDD RepositoryDDD Repository
DDD Repository
 
DDD Start Ch#3
DDD Start Ch#3DDD Start Ch#3
DDD Start Ch#3
 
실무로 배우는 시스템 성능 최적화 Ch8
실무로 배우는 시스템 성능 최적화 Ch8실무로 배우는 시스템 성능 최적화 Ch8
실무로 배우는 시스템 성능 최적화 Ch8
 
Logstash, ElasticSearch, Kibana
Logstash, ElasticSearch, KibanaLogstash, ElasticSearch, Kibana
Logstash, ElasticSearch, Kibana
 
실무로배우는시스템성능최적화 Ch1
실무로배우는시스템성능최적화 Ch1실무로배우는시스템성능최적화 Ch1
실무로배우는시스템성능최적화 Ch1
 
HTTP 완벽가이드 21장
HTTP 완벽가이드 21장HTTP 완벽가이드 21장
HTTP 완벽가이드 21장
 
HTTP 완벽가이드 6장.
HTTP 완벽가이드 6장.HTTP 완벽가이드 6장.
HTTP 완벽가이드 6장.
 
HTTP 완벽가이드 1장.
HTTP 완벽가이드 1장.HTTP 완벽가이드 1장.
HTTP 완벽가이드 1장.
 
Cluster - spark
Cluster - sparkCluster - spark
Cluster - spark
 
Pair RDD - Spark
Pair RDD - SparkPair RDD - Spark
Pair RDD - Spark
 

Recently uploaded

Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)Wonjun Hwang
 
MOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution DetectionMOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution DetectionKim Daeun
 
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...Kim Daeun
 
A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)Tae Young Lee
 
캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차캐드앤그래픽스
 
Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)Wonjun Hwang
 

Recently uploaded (6)

Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)
 
MOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution DetectionMOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution Detection
 
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
 
A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)
 
캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차
 
Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)
 

C++ api design 품질

  • 1. C++ API 디자인 Ch.2 품질 아꿈사 Cecil
  • 2. 이 장에서는 좋은 API가 갖춰야 할 기본 품질은?
  • 3. 고품질의 API 설계를 가능하게 하는 특정한 품질 속성이 존재하고, 가능하다면 이 기준을 도입해라 ! 설계 품질을 낮추는 많은 요인은 반드시 피해라
  • 4. 고품질 API 설계를 위한 이 책에서 제시하는 기준 • 문제 도메인 • 구체적인 구현 숨기기 • 작게 완성하기 • 쉬운 사용성 • 느슨한 연결 • 안정화와 문서화, 테스트
  • 5. 문제 도메인 모델 API는 문제에 대한 납득할 만한 해결책을 제공해야 한다
  • 6. 훌륭한 추상화 제공 • API는 논리적인 추상화를 제공해야 함 • • 저수준의 코드 구현 보다 추상화된 API SET을 제공 But, 훌륭한 추상화 제공은 쉽지 않은 문제 • 일관성 있고 충분한 논리성을 바탕으로 API를 개발해야 함
  • 7. 핵심 객체 모델링 • API는 문제 도메인의 핵심 객체를 모델링 해야함 • • 객체 모델링의 주요 목표: 주요 객체 식별 및 연결 관계 확인 API의 핵심 모델은 시간이 지나면 변화가 필요함을 고려해야 함 • 너무 앞서서 필요한 것보다 일반적인 객체 생성은 금물
  • 8. 구체적인 구현 숨기기 클라이언트에 영향을 미치지 않고, 내부 로직을 변경할 수 있도록 구체적인 구현을 숨겨야 한다
  • 9. 물리적 은닉: 선언 vs 정의 • 선언: 컴파일러에서 심벌의 타입과 이름을 알려줌 • • 정의: 전체적인 세부 사항을 제공 • • 변수 정의 및 함수의 본문 물리적 은닉 • • ex) external int i, class MyClass … 공개된 인터페이스로부터 분리된 파일에 상세한 내부 로직을 구현 가급적이면 API 헤더는 선언만을 제공 • inline 함수 사용 자제
  • 10. 논리적 은닉: 캡슐화 • API의 구체적인 로직이 외부로 노출되지 않도록 접근 제한자를 사용 • C++ 접근 제한자 • public: 외부에서 접근이 가능 • 구조체의 기본 접근 수준 • protected: 클래스와 파생된 클래스 내부에서만 접근 가능 • private: 클래스 내부에서만 접근 가능 • 클래스의 기본 접근 수준
  • 11. 멤버 변수 감추기 • 멤버 변수의 직접 노출보다 getter/setter 메서드를 제공 • getter/setter의 장점 • • 지연된 평가 • 캐싱 • 추가 연산 • 알림 • 디버깅 • 동기화 • 훌륭한 접근 제어 • • 유효성 체크 바뀌지 않는 관계 유지 클래스의 데이터 멤버는 항상 private로 선언
  • 12. 메서드 구현 숨기기 • public으로 선언할 필요가 없는 메서드를 감추는 것이 중요 • 클래스는 “무엇을 할 것인지를 정의하는 것” • C++의 제약 사항 • 클래스를 구현하기 위해 필요한 모든 멤버를 헤더에 선언 해야함 • 해결방안 • • Pimple 이디엄 사용 or cpp 파일에 정적 함수를 사용 Tip: private 멤버에 대한 비상수 포인터나 참조를 리턴(X:캡슐화 위반)
  • 13. 클래스 구현 숨기기 • 실제 구현 코드를 가능하면 감춰라. #include<vector> ! class Fireworks { public: Fireworks(); ! ! ! }; void void void void SetOrigin(double x, double y); SetColor(float r, float g, float b); SetGravity(float g); SetNumberOfParticles(int num); void Start(); void Stop(); void Next Frame(float dt); private: class FireParticle { public: double mX, mY; double mLifeTime; }; double mOriginX, mOriginY; float mRed, mGreen, mBlue; float mGravity; float mSpeed; bool mIsActive; std::vector<FireParticle *> mParticles; FireParticle을 외부에 구현하기 보다는 내부 클래스로 구현
  • 14. 작게 완성하기 좋은 API라면 최소한의 크기로 완성되어야 한다
  • 15. 지나친 약속은 금지 • API의 모든 public 인터페이스는 약속이다 • 새로운 기능 추가는 쉽지만 기존의 것의 변경이 어렵다 • “더더더” 일반적인 해결책을 찾기 위한 노력을 피해야 하는 이유 • 보다 더한 일반화가 필요한 순간은 오지 않을 수 있다 • 만약 그런날이 온다면, 경험으로 인한 더 좋은 해결책을 내놓을 수 있다 • 추가 기능이 필요하다면, 복잡한 곳보다는 간단한 API에 추가하는 것이 쉽다
  • 16. 가상 함수의 추가는 신중하게 • 상속과 추상화 • • 의도 했던 것 보다 더 많은 기능들을 노출시킬 수 있는 방법 상속의 함정 • “깨지기 쉬운 베이스 클래스 문제”: 베이스 클래스의 변경이 클라이언트에 영향을 줌 • 클라이언트는 API 개발자가 의도치 않았던 방법으로 API를 사용할 수 있다 • 클라이언트는 API를 오류가 많이 발생하도록 확장할 수 있다 (동기화 등) • 클래스 통합을 방해: 기존 함수의 정책에 위반되는 행위를 수행할 경우
  • 17. C++ 가상 함수 사용시 생각할 점 • 가상 함수 호출은 런타임시 vtable을 탐색 • 가상함수를 사용할 수록 객체의 크기도 비례해서 증가(vtable) • 가상 함수는 인라인이 될 수 없다 • 가상 함수를 오버로드 하는 것은 어렵다 • 추가적으로 기억할 것 • 소멸자는 항상 가상 함수로 선언 • 메소드 호출 관계를 문서화 • 생성자나 호출자에서는 절대 가상함수를 호출하지 않음
  • 18. 편리한 API • 기능에 초점을 맞춘 순수 API 제공 vs. 편리한 래퍼 API 제공 • 순수 API 제공 • • 래퍼 API 제공 • • 경량화되고 기능에 집중, 구현 코드의 복잡성을 줄임 클라이언트는 적은 양의 코드를 통해서 기본적인 기능이 동작 2.4 Easy to use 39 최소화시킨 핵심 API를 기반으로 분리된 모듈이나 라이브러리를 통해서 사용하기 편리한 API를 제공 FIGURE 2.4 An example of a core API (OpenGL) separated from convenience APIs layered on top of it (GLU and GLUT). GLUquadric *qobj gluNewQuadric();
  • 19. 쉬운 사용성 잘 설계한 API라면 간단한 작업을 쉽고 명확하게 만들어야 한다
  • 20. 한눈에 들어오는 • 사용자가 API를 어떻게 사용해야 할지 한눈에 이해 • 클래스와 함수 이름을 잘 선택해서 직관적이고 논리적인 객체를 모델 • 자세한 내용은 4장에서
  • 21. names in Chapter 4 when I discuss API design techniques. Avoiding the use of abbreviations can also play a factor in discoverability (Blanchette, 2008) so that users don’t have to remember if your API uses GetCurrentValue(), GetCurrValue(), GetCurValue(), or GetCurVal(). 2.4.2 Difficult to Misuse A good 사용하기에도 use, should also be difficult being easy to 어렵게 Scott 잘못API, inis addition toimportant general interface design guideline to misuse.2004). Meyersofsuggests that this the most (Meyers, Some the most common ways to misuse an API include passing the wrong arguments to a method or passing illegal values to a method. These can happen when you have multiple arguments of the same type and the user forgets the correct order of the arguments or where you use an int to represent a small •range of values instead of a more constrained enum type (Bloch, 2008). For example, consider the following method signature: 좋은 API라면 잘못 사용하기에도 어려워야 함 std::string FindString(const std::string &text, bool search forward, <boolean 사용> bool case sensitive); 2.4 Easy to use 41 ! It would be easy for users to forget whether the first bool argument is the search direction or the caseenum SearchDirection { the flags in the wrong<enum 사용> sensitivity flag. Passing order would result in unexpected behavior and FORWARD, probably!cause the user to waste a few minutes debugging the problem, until they realized that they BACKWARD had }; transposed the bool arguments. However, you could design the method so that the compiler catches this kind of error for them by introducing a new enum type for each option. For example, enum CaseSensitivity { ! CASE SENSITIVE CASE INSENSITIVE }; std::string FindString(const std::string &text, SearchDirection direction, CaseSensitivity case sensitivity); ! • Not only does this mean that users cannot mix up the order of the two flags, because it would generate a compilation error, but also the code they have to write is now more self-descriptive. Compare 코드의 가독성을true, false); 위해서 Boolean보다는 enum을 사용 높이기 result FindString(text, with • Tip: 함수에 같은 타입의 파라미터를 여러개 사용하지 말자 result FindString(text, FORWARD, CASE INSENSITIVE); TIP Prefer enums to booleans to improve code readability.
  • 22. abbreviations in several of its method names, such as prevValue() and previousSibling(). This is another example of why the use of abbreviations should be avoided at all costs. use 2.4 Easy to 43 The use of consistent method signatures is an equally critical design quality. If you have several methods that accept similar argument lists, you should endeavor to keep a consistent number and order for those arguments. To give a counterexample, I refer you to the following functions from 2.4.3 Consistent the standard C library: 일관성 있는 A good API should apply a consistent design approach so that its conventions are easy to remember, and void bcopy(const void *s1, void 2008).size applies to all aspects of API design, such as therefore easy to adopt (Blanchette, *s2, This t n); char *strncpy(char *restrict s1, const char *restrict s2, size t n); • naming conventions, parameter order, the use of standard patterns, memory model semantics, the use Both of these error handling, and so on. of exceptions, functions involve copying n bytes of data from one area of memory to another. In terms of bcopy() these, consistent data from s1 into imply reuse strncpy() copies the However, the the first offunction copies naming conventions s2, whereasof the same words forfrom s2 into same concepts across the API. For example, if you have decided to use the verb pairs Begin and End, s1.•This can give rise to subtle memory errors if a developer were to decide to switch usage between you should not mingle the terms Start and Finish. As another example, the Qt3 API mixes the use of the two functions without a methodreading such as respective man pages. To be sure, there is a clue to close names, of the prevValue() and previousSibling(). This is abbreviations in several of its the conflicting specifications in the function signatures: note the usecosts. const pointer in each case. another example of why the use of abbreviations should be avoided at all of the However, thisconsistent method signatures is won’t be caught by a compiler if the source pointer is not The use of could be missed easily and an equally critical design quality. If you have several declared thatbe const. methods to accept similar argument lists, you should endeavor to keep a consistent number and • order for also the inconsistent useaof the words “copy” and “cpy.” following functions from Note those arguments. To give counterexample, I refer you to the the Let’s take another example from the standard C library. The familiar malloc() function is used to standard C library: API 설계 관점에서의 일관성 명명 규칙, 파라미터의 순서, 표준 패턴의 사용, 의미론적인 메 모리 모델, 예외 및 오류 처리 등. 일관성을 지키지 못한 사례 allocate bcopy(const void *s1, void *s2, and then); void a contiguous block of memory, size t calloc() function performs the same operation with the!char *strncpy(char *restrict s1, const char *restrict zerosize t n); addition that it initializes the reserved memory with s2, bytes. However, despite their similar purpose, they have different function signatures: Both of these functions involve copying n bytes of data from one area of memory to another. However,*calloc(size t count, size t size); s2, whereas strncpy() copies from s2 into void the bcopy() function copies data from s1 into s1.!This can give rise to subtle memory errors if a developer were to decide to switch usage between void *malloc(size t size); the two functions without a close reading of the respective man pages. To be sure, there is a clue to Theconflicting specifications in the a size insignatures:bytes, whereasthe const pointer in eachcount * size) the malloc() function accepts function terms of note the use of calloc() allocates ( case. bytes. In this could be missed inconsistent, this violatesby a compiler if the least surprise.isAs a further However, addition to being easily and won’t be caught the principle of source pointer not •example,tothe const. and write() standard C functions accept a file descriptor as their first paradeclared be read() Note also the the fgets() and fputs() “copy” and “cpy.” meter, whereas inconsistent use of the words functions require the file descriptor to be specified last Let’s take another (Henning, 2009). example from the standard C library. The familiar malloc() function is used to Tip: 함수의 이름, 파라미터의 순서를 일관성 있게 유지하라
  • 23. 44 CHAPTER 2 Qualities 클래스 수준의 일관성 • • • • The STL is a great example of this. The std::vector, std::set, std::map, and classes all offer a size() method to return the number of elements in the cont also all support the use of iterators, once you know how to iterate through a apply the same knowledge to a std::map. This makes it easier to memorize the pr 비슷한 기능을 제공하는 클래스들은 비슷한 인터페이스를 of the API. this kind of consistency for free through polymorphism: by placin 제공 get You tionality into a common base class. However, often it doesn’t make sense for inherit from a common base class, and you shouldn’t introduce a base class pure • ex) STL, std::vector, std::set, std::map의 size 함수 it increases the complexity and class count for your interface. Indeed, it’s n as STL container classes do not inherit from a common base class. Instead, yo design for this by manually identifying the common concepts across your cla 다형성을 적용하면 일관성 얻기가 용이 same conventions to represent these concepts in each class. This is often re polymorphism. You can also make use of C++ templates to help you define and apply this k For example, you could create a template 표현: coordinate class 공통 베이스 클래스를 둘 수 없는 경우에도 각 클래스에 공통된 개념들을 같은 이름으로 for a 2D 정적 다형성and specia floats, and doubles. In this way you are assured that each type of coordinate offe interface. The following code sample offers a simple example of this: C++ 템플릿을 사용한 일관성 유지 • ex) Coord2D<int>, Coord2D<float> .. template <typename T> class Coord2D { public: Coord2D(T x, T y) : mX(x), mY(y) {}; T GetX() const { return mX; } T GetY() const { return mY; } void SetX(T x) { mX void SetY(T y) { mY x; } y; } void Add(T dx, T dy) { mX þ dx; mY þ dy; } void Multiply(T dx, T dy) { mX * dx; mY * dy; } private: T mX; T mY; }; With this template definition, you can create variables of type Coord2D<int>, Coo Coord2D<double> and all of these will have exactly the same interface.
  • 24. 수직적인 • 다른 코드에 영향을 미치지 않는 함수 • • ex) 속성 값을 할당하는 메서드 호출은 그 속성 값만 변화 수직적 API 설계시 고려할 사항 • 중복 제거: 같은 정보가 2가지 이상의 방법으로 반복되지 않게 함 • 독립성 증가: 모든 중첩되는 개념들은 각각의 기반 컨포넌트로 분리 float CheapMotelShower::GetTemperature() const { return mTemperature; } ! float CheapMotelShower::GetPower() const { return mPower; } ! void CheapMotelShower::SetPower(float p) { if (p < 0) p = 0; if (p > 100) p = 100; mPower = p; mTemperature = 42.0f þ sin(p/38.0f) * 45.0f; } float IdealShower::GetTemperature() const { return mTemperature; } ! float IdealShower::GetPower() const { return mPower; } ! void IdealShower::SetTemperature(float t) { if (t < 42) t = 42; if (t > 85) t = 85; mTemperature = t; } void IdealShower::SetPower(float p) { if (p < 0) p = 0; if (p > 100) p = 100; mPower = p; }
  • 25. 견고한 자원 할당 • C++의 메모리 관련 이슈 • • 메모리 이중 해제 • 할당자 혼용 • 잘못된 배열 해제 • • Null 역참조 메모리 누수 관리되는 포인터를 사용 • 공유 포인터: boost::shared_ptr, … • 약한 포인터: boost::weak_ptr, … • 범위 한정 포인터: boost::scoped_ptr, …
  • 26. 플랫폼 독립적 • bool StartCall(const std::string &number); bool EndCall(); #if defined TARGET OS IPHONE bool GetGPSLocation(double &lat, double &lon); #endif }; This poor design creates a different API on different platforms. Doing so your API to introduce the same platform specificity into their own applicatio 2.4 Easy to use 51 the aforementioned case, your clients would have to guard any calls to GetGPSL cisely the same #if conditional statement, otherwise their code may fail to co fined symbol error on other platforms. Furthermore, if in a later version of the API you also add support for anot 2.4.6 Platform Independent Windows Mobile, then you would have to update the #if line in your publ A well-designed C++ API should always avoid platform-specific #if/#ifdef lines in its public. head- your clients would have to find all instances in their co WIN32 WCE Then, ers. If your API presents a high-level and logical model for your problem domain,embedded the TARGET OS IPHONE define and extend it to also include WIN32 as it should, there are very few cases where the API should be different for different platforms. About the only cases you have unwittingly exposed the implementation details of your API. where this may be appropriate are when you are writing an API to interface with a platform-specific Instead, you should hide the fact that the function only works on certain pla resource, such as a routine that draws in a window and requires the appropriate window handle to be method to determine whether the implementation offers the desired capabilitie passed in for the native operating system. Barring these kinds of situations, you shouldFor example, form. never write public header files with platform-specific #ifdef lines. class MobilePhone For example, let’s take the case of an API that encapsulates the functionality offered by a mobile phone. Some mobile phones offer built-in GPS devices that can deliver the geographic location of 2 Qualities 52{ CHAPTER public: the phone, but not all devices offer this capability. However, you should never expose this situation bool StartCall(const std::string &number); directly through your API, such as in the following example: 잘 설계된 API라면 특정 플랫폼에 독립적인 #if/#ifdef 코드를 public 헤더에 사용하지 않아야 함 class MobilePhone { public: bool StartCall(const std::string &number); bool EndCall(); #if defined TARGET OS IPHONE bool GetGPSLocation(double &lat, double &lon); #endif }; bool EndCall(); bool HasGPS() const; Now your API is consistent over all platforms and does not expose the details of wh bool GetGPSLocation(double &lat, double &lon); port GPS coordinates. The client can now write code to check whether the current }; GPS device, by calling HasGPS(), and if so they can call the GetGPSLocation() the actual coordinate. The implementation of the HasGPS() method might look som bool MobilePhone::HasGPS() const { #if defined TARGET OS IPHONE return true; #else This poor design creates a different API on different platforms. Doing so forces the clients of return false; your API to introduce the same platform specificity into their own applications. For example, in #endif the aforementioned case, your clients would have to guard any calls to GetGPSLocation() with pre} cisely the same #if conditional statement, otherwise their code may fail to compile with an unde- This is far superior to the original design because the platform-specific #if statem fined symbol error on other platforms. in the .cpp file instead of being exposed in the header file. Furthermore, if in a later version of the API you also add support for another device class, say Windows Mobile, then you would have to update the #if line in your public header to include TIP WIN32 WCE. Then, your clients would have to find all instances in their code where they have This is platform embedded the TARGET OS IPHONE define and extend it to also include WIN32 WCE.Never put because specific #if or #ifdef statements into your public APIs. It exposes impleme you have unwittingly exposed the implementation details of your API. makes your API appear different on different platforms. Instead, you should hide the fact that the function only works on certain platforms and provide a
  • 27. 느슨한 연결 좋은 API는 느슨한 연결과 높은 결합성을 보인다
  • 28. 이름만을 사용한 연결 • 클래스 전체 선언을 참조할 필요가 없다면 전방 선언을 사용 class MyObject; // only need to know the name of MyObject ! class MyObjectHolder { public: MyObjectHolder(); void SetObject(MyObject *obj); MyObject *GetObject() const; private: MyObject *mObj; };
  • 29. 클래스 연결 줄이기 • 연결 관계를 줄이기 위해 멤버 함수 대신 비멤버, 비프렌드 함수 사용 // myobject.h class MyObject { public: void PrintName() const; std::string GetName() const; ... protected: ... private: std::string mName; ... }; // myobject.h class MyObject { public: std::string GetName() const; ... protected: ... private: std::string mName; ... }; ! void PrintName(const MyObject &obj);
  • 30. 의도적인 중복 • 심각한 연결 관계를 잘라내기 위해 적은 양의 중복코드를 추가하 는 것이 효과적일 경우 #include "ChatUser.h" #include <string> #include <vector> class TextChatLog { public: bool AddMessage(const ChatUser &user, const std::string &msg); int GetCount() const; std::string GetMessage(int index); private: struct ChatEvent { ChatUser mUser; std::string mMessage; size t mTimestamp; }; std::vector<ChatEvent> mChatEvents; }; #include <string> #include <vector> class TextChatLog { public: bool AddMessage(const std::string &user, const std::string &msg); int GetCount() const; std::string GetMessage(int index); private: struct ChatEvent { std::string mUserName; std::string mMessage; size t mTimestamp; }; std::vector<ChatEvent> mChatEvents; }; ChatUser에 대한 연결 관계가 없어짐
  • 31. 58 CHAPTER 2 Qualities 2.5.4 Manager Classes A manager class is one that owns and coordinates several lower-level classes. This can be used to break the dependency of one or more classes upon a collection of low-level classes. For example, consider a structured drawing program that lets you create 2D objects, select objects, and move them around a canvas. The program supports several kinds of input devices to let users select and move objects, such as a mouse, tablet, and joystick. A naive design would require both select and move operations to know about each kind of input device, as shown in the UML diagram (Figure 2.5). Alternatively, you could introduce a manager class to coordinate access to each of the specific input device classes. In this way, the SelectObject and MoveObject classes only need to depend on this single manager class, and then only the manager class needs to depend on the individual input device classes. This may also require creating some form of abstraction for the underlying classes. For example, note that MouseInput, TabletInput, and JoystickInput each have a slightly different interface. Our manager class could therefore put in place a generic input device interface that abstracts away the specifics of a particular device. The improved, more loosely coupled, design is shown in Figure 2.6. Note that this design also scales well too. This is because more input devices can be added to the system without introducing any further dependencies for SelectObject or MoveObject. Also, if you decided to add additional manipulation objects, such as RotateObject and ScaleObject, they only need a single dependency on InputManager instead of each introducing further coupling to the underlying device classes. 매니저 클래스 • 하위 수준에 여러개의 클래스를 포함하면서 중재하는 역할을 수행 • 여러개의 클래스들을 대상으로 하나 혹의 그 이상의 의존성을 줄임 2.5 Loosely coupled MoveObject SelectObject + DoMove() : void + DoSelect() : void MoveObject SelectObject + DoMove() : void + DoSelect() : void InputManager + GetXCoord() : integer + GetYCoord() : integer + IsButton1Down() : boolean + IsButton2Down() : boolean + IsButton3Down() : boolean MouseInput + GetXCoord() : integer + GetYCoord() : integer + IsLMBDown() : boolean + IsMMBDown() : boolean + IsRMBDown() : boolean TabletInput + GetXCoord() : integer + GetYCoord() : integer + IsPenDown() : boolean JoystickInput + GetXCoord() : integer + GetYCoord() : integer + IsTriggerDown() : boolean MouseInput + GetXCoord() : integer + GetYCoord() : integer + IsLMBDown() : boolean + IsMMBDown() : boolean + IsRMBDown() : boolean FIGURE 2.5 TabletInput + GetXCoord() : integer + GetYCoord() : integer + IsPenDown() : boolean Multiple high-level classes each coupled to several low-level classes. FIGURE 2.6 Using a manager class to reduce coupling to lower-level classes. JoystickInput + GetXCoord() : integer + GetYCoord() : integer + IsTriggerDown() : boolean 59
  • 32. 콜백과 옵저버, 알림 • 이벤트가 발생했을때 이를 다른 클래스에 알리는 API • 일반적인 이슈 • 재진입성 • • 수명 관리 • • 알림을 받은 코드가 다시 API를 호출하는 경우를 고려 이벤트에 대한 구독/해지 기능 제공, 또한 중복 이벤트를 받지 않도록 고려 이벤트 순서 • 이벤트의 순서를 명확히 정의 (네이밍 등의 방법으로 명시)
  • 33. 콜백 • 저수준 코드가 고수준의 코드를 사용할때 의존성을 없애는 방법 • 대규모 프로젝트에서 순환 의존 고리를 제거 ! ! ! ! • #include <string> class ModuleB { public: typedef void (*CallbackType)(const std::string &name, void *data); void SetCallback(CallbackType cb, void *data); ... private: CallbackType mCallback; void *mClosure; }; ! if (mCallback) { (*mCallback)("Hello World", mClosure); } 객체지향 프로그램에서 인스턴스 메소드를 콜백으로 사용하는 경우 this 객 체 포인터를 전달해야 함
  • 34. 옵저버 • 콜백은 객체지향에서는 좋은 해결책이 되지 못함 • • this 이슈 좀 더 객체지향적인 해결책 옵저버 • 3장과 4장에서 …
  • 35. 알림 • 콜백과 옵저버는 특정 작업을 위해 생성 • 알림 • 시스템에서 연결되지 않은 부분 사이에서 알림이나 이벤트를 보내는 메커니즘을 중앙화 • 보내는 이와 받는 이 사이의 의존성이 전혀 없음 • ex) signal/slot class MySlot { public: void operator()() const { std::cout << "MySlot called!" << std::endl; } }; // Create an instance of our MySlot class MySlot slot; ! // Create a signal with no arguments and a void return value boost::signal<void ()> signal; ! // Connect our slot to this signal signal.connect(slot); ! // Emit the signal and thereby call all of the slots signal();
  • 36. 안정화와 문서화, 테스트 ! 좋은 API라면 - 안정적이어야 하고, 미래 증명적이어야 함. - 명확히 이해할 수 있게끔 구체적인 문서를 제공해야 함 - 변경이 발생하더라도 기존 기능에 영향이 가지 않도록 자동화된 테스트 프로세스를 갖추어야 함
  • 37. 결론 가능하다면 고품질 API 설계를 가능케하는 기준들을 도입하고, 품질을 낮추는 많은 요인들은 반드시 피해라
  • 38. But, 아무리 잘 정리된 규칙도 모든 상황에 적용 불가 각 프로젝트 특성을 고려한 다음 가장 적합한 해결책을 적용
  • 39. References • Martin Reddy (2014). C++ API 디자인. (천호민, 옮김). 고양시: 지앤선. (원서출판 2013)