2012년 5월 30일 수요일

읽기 좋은 코드가 좋은 코드다 (한빛미디어)



"읽기 쉬운 100줄의 코드는 읽기 어려운 50줄의 코드에 비해서 훨씬 낫다"

회사의 프로젝트를 수행하면서 직접 개발을 하는 경우가 많지만, 이에 못지않게 이미 개발된 소스코드를 분석하는 일도 많다.
남의 소스코드를 분석하다보면 잘 이해가 되는 소스가 있는 반면, 여러번 봐야하고, 심지어 필기를 해가며 분석을 해가야 이해가 되는 소스가 있다. 함수가 어떤 동작을 수행하는지 알기 위해 코드 한줄 한줄을 다 읽어야 이 함수가 무슨 일을 하는 지 알수 있다면 이 코드의 분석은 정말 끔찍하고 피하고 싶은 일이 될것이다.
이 책을 읽고 난 뒤, "이 코드는 이 부분 때문에 가독성이 참 떨어지네" 라고 잡아낼 수 있는 감이 생긴 것 같다. 물론 직접 코딩을 할 때도 많은 도움이 될 것이다.

이 책은 얇다. 삽화, 소스코드, 글 들이 잘 배치가 되어있어서 지루하지않고 재미있게 읽을 수 있고, 250페이지 정도여서 짧은 시간에 읽을 수 있는 분량이다.
저자들은 예제를 통해서 이 코드는 왜 나쁜지, 어떻게 하면 읽기 좋은 코드가 되는지를 설명하고 있다. 저자들이 직접 개발했던 코드들, 오픈소스 코드들(크로미움 프로젝트 등)에서 어떠한 부분이 읽기 좋지 않았고, 어떻게 개선이 되서 읽기 좋은 코드가 되었는지 잘 설명을 해주고 있다. 설명을 위해 만든 코드가 아닌, 문제가 있었던 코드들을 찾아서 사용하고 있어서 그런지 예제들이 참 좋았던 것 같다.

이 책은 총 네 파트로 구성 되어있다.
첫번째 파트 (표면적 수준에서의 개선) - 파트 이름대로 표면적 수준에서의 개선할 수 있는 내용들을 설명하고 있다. 변수명, 함수명, 주석등을 통해 코드를 접했을 때 바로 보이는 부분들을 다루고 있다.
두번째 파트 (루프와 논리를 단순화하기) - 조건문, 루프 (do/while, goto)를 분석하기 쉽게 구성하는 방법, 큰 논리를 단순한 작은 논리로 단순화하여, 읽는 사람이 쉽게 소화할 수 있는 방법을 제시하고 있다.
세번째 파트 (코드 재작성하기) - 큰 흐름과 관계없는 작은 문제들로 쪼갠다음 분리하고, 작업은 한번에 하나씩이라는 원리를 토대로 작업을 재정리하는 방법을 다루고 있다.
네번째 파트 (선택된 주제들) - 이 파트에서는 테스트 코드의 가독성에 대해 다루고, 분/시간 카운터 설계 및 구현과정을 진행하면서 어떻게 가독성을 높여가는지 보여주고 있다.

소프트웨어 엔지니어라는 직업을 가지고도 변수 명, 변수 선언 위치 등에 대해 고민하고 구현을 했었던 적이 과연있었나 싶고, 부끄럽기까지하다. 이 책의 내용들을 통째로 머릿속에 집어넣고 다니고 싶다. 코드를 작성하는 사람이 아닌 코드를 읽는 사람의 입장에서 구현을 하라는 말을 명심한다면 읽기 좋은 코드를 작성할 수 있을 것이다.

자신의 코드를 남에게 보이기 부끄러웠다면, 당장 이 책을 사서 보길 바란다.

2012년 5월 16일 수요일

Chromium - Notification Service

Overview

Notification Service provides notification delivery mechanism from producer to observer in the same thread. This class is created and used in browser process’s sub threads.
Refer to src/content/browser/browser_process_sub_thread.cc






Components

a. NotificationService
It handles notification delivery and manages observer list.
This service provides method that can add or remove notification for observer.
Also, Notify method is provided for notification provider.
To get the NotificationService object, content::NotificationService::current() is used.
It could be used in that thread only. It means calling content::NotifcationService::current()
returns different object in UI thread and IO thread.

b. Notification Observer:
This component is responsible for registration its preferred notification type to
NotificationService.
For receiving notification, Observer must implement NotificationObserver class.

c. Notification Provider:
It is a notification producer. This component sends notification to NotificationService.


How To Register Notification to NotificationService

a. Add Notification
For registration, NotificationRegistrar class is used instead of using NotificationService
directly. It is a helper class for registering notification and ensures that all registered
notifications are unregistered when the class is destroyed.
Refer to src/content/public/browser/notification_registrar.h

b. Implement Notification Handler
For implementing handler, Observer should implement NotificationObserver class as a base
class. It has only one function, observe(). When registered notification is occured,
NotificationService calls this function.
Refer to src/content/public/browser/notification_observer.h


How to Notify to NotificationService

NotificationService provides Notify() for synchronous notify mechanism. Notification provider should deliver notification type, source that originates this notification and details that contains additional information to NotificationService via Notify().
Refer to src/content/browser/notification_service_impl.cc 

2012년 5월 8일 화요일

Chromium Bug - 82276 (3)

이제 FindBarController가 마우스 클릭 이벤트를 구독하도록 하고, 이벤트 핸들러를 구현해보자.

이전 포스트에서 언급했듯이 Find Bar의 life cycle은 FindBarController 의 Show()/EndFindSession() 에서 시작되고 종료된다.

Show() 함수에서 다음과 같이 마우스 클릭 이벤트를 등록했다.


registrar_ (content::NotificationRegistrar) 의 Add() 를 이용해서 Noti를 받고싶은 원하는 notification type을 등록한다.
Add()의 첫번째 파라미터로 NotificationObserver를 구현한 객체를 전달하는데,
Notificaiton Service (NotificationServiceImpl) 에게 이 Noti가 발생했을때, 이 객체의 Observe()를 호출하라는 의미이다.
두번째 파라미터는 Noti의 이름이고, 세번째 파라미터는 받고싶은 Noti의 Source를 설정하는 부분이다.
Noti의 Source란 이 Noti를 생성하는 부분인데, 두번째 파라미터의 Noti를 Observer 가 특정 객체로부터 생성된 것만 받거나 모든 객체에서 생성되것을 받을 수 있다.
다음과 깉이 설정하면 WebContents의 객체에서 Noti만 받을 수 있다.
content::Source(tab_contents_->web_contents())
위 그림에서 처럼 AllSources() 로 설정하면, 모든 객체에서 생성되는 INPUT_EVENT_ACK Noti를 받을 수 있다.


다음은 Observe() 함수에 추가한 부분이다.



Observe() 함수의 파라미터를 통해 type, source, details를 받을 수 있다.
details는 이 Noti가 전달해주는 추가 데이터이다. INPUT_EVENT_ACK의 경우 인풋 이벤트의 종류이다.
여기서는 Mouse Up 이벤트만 관심있으므로 datails 를 통해 인풋 이벤트중 원하는 이벤트를 선택할 수 있다.

마지막으로 이벤트 등록을 삭제하는 부분이다.


삭제도 물론 NotificationService 로부터 이루어지므로 registrar_의 Remove() 를 통해 이루어지고, 파라미터는 Add() 와 같다.


이것으로 Find Refresh 에 필요한 이벤트 등록이 완료됐다.
다음 포스트에서는 어떻게 Refresh를 할지 고민해보자.

2012년 5월 4일 금요일

Chromium Bug - 82276 (2)

Chromium - Notification Registration/Handler

이 버그를 어떻게 풀어나갈지 고민 중이다.

우선 Find Bar를 언제 Refresh 해야할까? (여기서 Refresh란 다시 find 동작을 수행하는 것이다.)
웹페이지의 Re-Paint 이벤트는 이 상황에서 적당한 이벤트 일까?
Re-Paint 이벤트는 웹페이지 내 커서만 깜빡여도 발생하므로 Refresh 가 쉴새 없이 진행될 것이다.
따라서 Refresh 이벤트로 사용하기에는 부적절한 것 같다.
이 경우에 쓸만할 적당한 이벤트가 무엇이 있을까?
흠... 이 버그를 보이게 한 시작점은 폴딩된 메일의 클릭이므로, 마우스 클릭 이벤트를 Refresh 이벤트로 사용해보자!

소스코드를 뒤진 결과 다음과 같은 이벤트가 있다.
content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK
이 이벤트는 RenderWidgetHostImpl 객체에서 인풋 이벤트 처리가 끝난 후, Notify 하고 있다.
인풋 이벤트 처리 후가 Refresh 시점에 적절한 지는 좀 더 생각을 해봐야하겠지만, 우선은
인풋 관련 해서 현재 Noti 하고있는 이벤트는 이것뿐인 것 같으므로 사용해보자.

그렇다면 이 이벤트는 언제 받아보면 될까?
브라우저가 실행되고 있다고 해서 이 인풋 이벤트를 받아볼 필요는 없다.
Find Bar 가 떠 있는 상태에서만 인풋 이벤트를 받아서 처리하면 될 것이다.
그렇다면 Find Bar의 보이기/사라지기 가 수행되는 시점을 찾아서 보이기 시점에 인풋 이벤트를 구독하고
사라지는 시점에 구독하던 이벤트를 제거하면 될 것 같다.
Find Bar는 FindBarController::Show()에서 FindBar를 보이게 하고, FindBarController::EndFindSession()에서 사라지게 하고 있다.
따라서 인풋 이벤트는 Show() 호출 시 이벤트와 핸들러를 등록하고, EndFindSession() 불릴 때, 제거하면 될 것같다.

그렇다면...
Chromium은 이벤트 등록/핸들러를 어떻게 할까?

원하는 이벤트의 등록을 위해 NotificationRegistrar 클래스를 제공하고 있다.
이 클래스는 content/public/browser/notification_registrar.h 에 정의되어 있다.
이벤트 핸들러구현을 위해서는 NotificationObserver 클래스를 제공하고 있고,
content/public/browser/notification_observer.h 에 정의되어 있다.

원하는 이벤트 등록을 위해서는 NotificationRegistrar 클래스를 사용해야한다.
아래 그림은 content::NotificationRegistrar 클래스 이다.
public 함수들을 보면 Add(), Remove(), RemoveAll() 등등 이벤트 등록에 필요한 함수들이
구현되어 있음을 알 수 있다.



이벤트 핸들러 등록을 처리하는 NotificationObserver 에 대해서 알아보자.
아래 소스를 보면 NotifiationObserver 는 추상클래스 이다.


핸들러 등록을 원하는 클래스에서 이 추상 클래스를 구현하면 등록한 이벤트가 발생했을때,
이 클래스의 Observe() 함수가 호출이 됨으로써 등록한 핸들러가 실행이 된다.

다음 포스트에서는 FindBarController 클래스의 Show()/EndFindSession()에서 이벤트의 등록/핸들러 구현을 해보겠다.