assert

종종 assert를 쓸까 말까 고민할 때가 있다. 아래 내용을 늘 기억하도록.

  • if 문을 써서 프로그램을 exit 해도 되지만 왜 그렇게 되었는지는 assert를 쓰는 것이 더 명확하다. (NetWeaver에서는 Dump 화면과 똑같이 보이기 때문에 좀 시껍하긴 하다)
  • debugger를 통해서도 오류를 알 수 있지만 assert를 쓰면 문제가 발생한 시점과 원인을 명확히 드러낼 수 있다.
  • 많이 써도 성능에는 상관없다.
  • 절대로 발생해서는 안되는 조건에 대해서 사용하는 것이지 정상적인 에러 상황을 처리하는 문장이 아니니 오용하지는 말자.

사실 assert는 OOP에서 쓰기에 좀 문제가 있다. 이에 대한 해결방안으로 Design by contract가 있겠지만 – C#의 Code Contracts 같은 – ABAP에서는 아직 지원되지 않고 있는 듯 하다.

Event handing in ABAP Object

아래는 SAP Help에서 가져온 예제이다.

정리하면, 3가지 역할이 필요하다. 즉, 1) Event를 등록하고 2) Event를 발생(Raise)하고 3) 발생된 Event를 handling 하는 것. 여기서는 ‘Event Trigger’ 클래스에서 1)과 2)를, ‘Event Handler’ 클래스에서 3)을 처리하는 방식을 취하고 있다.

구체적으로 보면,

  • SET HANDLER 구문은 handler table를 만든다. 이 테이블은 ① handler methods의 이름과 ② 그 handler를 갖고 있는 클래스 인스턴스의 reference로 구성되어있다.
  • 상기 클래스 인스턴스는 초기화 되더라도 garbage collection 대상이 되지 않는다. Handler table에 등록되어 떡 버티고 있기 때문이다.
  • For static events, the system creates an instance-independent handler table for the relevant class.

Package example in ABAP, 두번째

지난 번과는 반대로, subpackage가 superpackage의 object를 억세스하려 할 경우를 생각해보자.

20100610_package_inf_1

  1. SE80 open.
  2. 먼저 subpackage와 superpackage 양쪽에 Package Interface가 있나 확인해보자. 없으면 만들자.
  3. superpackage의 Package Interface를 더블 클릭. 두 개의 탭이 나오는데 ‘Exposed Objects’를 클릭, 노출할 object를 추가한다. (드래그 앤 드롭으로 쉽게 된다)
  4. 20100610_package_inf_2

  5. 다음에 바로 옆의 탭인 ‘Attributes’ 클릭. 하단에 ‘Visible for subpackages’에 체크한다.
  6. subpackage 이름을 더블 클릭, ‘Dependency Control List’ 탭을 연다. Add 버튼을 누르고 superpackage의 Package Interface를 추가한다.
  7. 20100610_package_inf_3

The Iterator Pattern (in ABAP)

20100606_iterator

Iterator를 사용해서 구현과는 분리하여 하나 하나 요소들을 셀 수 있다.
아래 소스를 보면 loop를 돌 때 Iterator의 메소드만 이용하고 있으며 Aggregate 클래스가 어떤 식으로 구현되어 있는가 – 배열이든, 벡터든, Internal Table이든 – 는 상관없다. 즉 나중에 Aggregate의 요소 관리 방식을 얼마든지 바꿀 수 있다는 이야기.
이를 위해 아래와 같은 것들이 필요하다.

  • Iterator 인터페이스.
  • Aggregate 인터페이스. Iterator 인터페이스 타입의 객체를 리턴하는 메소드를 제공.
  • ConcreteIterator. Iterator 인터페이스를 구현한다. 구체적으로 어떤 놈을 어떻게 돌아야하는지 알고 있어야 하기 때문에 ConcreteAggregate의 객체를 내부에 가지고 있다.
  • ConcreteAggregate. Aggregate 인터페이스를 구현한다. 자신을 인자로 넘겨주면서 ConcreteIterator를 생성한다.

헷갈리기 쉬운게 Next()와 hasNext()인데,

  • Next() 호출시 현재 요소를 리턴하면서 내부적으로 다음 위치로 이동하게 된다.
  • hasNext()는 최후의 요소를 얻기 전에는 true를 리턴하고 최후의 요소를 얻은 후에는 false를 리턴한다. 다음에 Next()를 호출해도 괜찮은지 조사하는 메소드로 이해하면 된다.

아래는 해당 소스.

Defining Exception Classes

모든 exception class는 아래 3가지 class들 중 하나의 subclass가 되어야 한다. 이 3개의 class들은 CX_ROOT로부터 파생된 것이지만, 내가 직접 CX_ROOT로부터 파생된 class를 만들어 사용할 수는 없다.

  • CX_STATIC_CHECK
  • CX_DYNAMIC_CHECK
  • CX_NO_CHECK

언제 어떤 것을 사용하느냐 – 를 생각해보자. 약간씩 틀리긴 하다.

20100528_exception

Subclasses of CX_STATIC_CHECK
The relevant exception must either be handled, or propagated explicitly using the RAISING addition. If this is not the case, the syntax check displays a warning.
If you define your own exception classes, CX_STATIC_CHECK is defined as the superclass by default.

Subclasses of CX_DYNAMIC_CHECK
You must either handle them or explicitly propagate them using the RAISING addition. The difference is that this is not statically checked. No syntax warning is reported if the exception is neither handled nor propagated. If the exception is then raised, a runtime error occurs.
Typical examples of this situation are the predefined exceptions CX_SY_… for errors that occur in the runtime environment. These are usually subclasses of CX_DYNAMIC_CHECK.

Subclasses of CX_NO_CHECK
The corresponding exceptions cannot be propagated explicitly using the RAISING addition. These exceptions can be handled. Otherwise they are automatically propagated. Neither a syntax warning nor a runtime error is caused directly where it is raised. Instead all exceptions that are not handled somewhere in the call hierarchy are propagated up to the highest call level. If it is not caught there either, a runtime error occurs at that point.
Some predefined exceptions with the prefix CX_SY_… for error situations in the runtime environment are subclasses of CX_NO_CHECK.

여기를 참고하도록.

The Factory Method Pattern (in ABAP)

20100512_factory_method
Factory의 요점은 다음과 같다.

  • ‘Product를 만드는 것’과 ‘등록’의 구현은 하위 클래스에서 수행한다.
  • (new를 사용해서) 실제의 인스턴스를 생성하는 대신에 인스턴스 생성을 위한 메소드를 호출함으로서(create_product) 구체적인 클래스명에 의한 속박에서 벗어나고 있다.

Framework 쪽: ZCL_FACTORY

Framework 쪽: ZCL_PRODUCT

ZCL_IDCARD_FACTORY

ZCL_IDCARD

그리고… 실행 프로그램 ZP_FACTORY_METHOD

이 실행 프로그램은 철저하게 암기할 필요가 있겠다.

Package example in ABAP

20100512_package_1
먼저 TEST_FRAMEWORK.ZCL_PRODUCT를 상속받는 TEST_IDCARD.ZCL_IDCARD를 컴파일하면 서로 다른 Package로 인한 경고를 받게 된다.

  1. 일단 TEST_IDCARD의 억세스를 가능하게 하기 위해 TEST_FRAMEWORK 쪽에서 인터페이스를 만들어주어야 한다.
    ZPIF_TEST_FRAMEWORK란 Package Interface를 만들어주고 대외적으로 오픈할 object – 여기서는 ZCL_PRODUCT와 ZCL_FACTORY – 를 추가해준다.
  2. 이때 Derive subclasses 부분도 ‘Allow Usage’ 체크를 해주어야 한다.
  3. 20100512_package_2

  4. TEST_IDCARD의 DCL(Dependency Control List)를 연다. (SE80로 가서 해당 class를 더블클릭하면 관련 탭이 보인다) 여기에 앞서 생성한 Package Interface를 추가한다.

다음으로, TEST_FRAMEWORK와 TEST_IDCARD의 상위 Package인 TEST_FACTORY_PATTERN 아래의 리포트 ZP_FACTORY_METHOD가 하위 Package들의 object를 억세스하게 하기 위해 아래와 같은 조치가 필요하다.

  1. ZPIF_TEST_FACTORY_METHOD라는 상위 Package의 Package Interface를 만든다. 여기에는 아무 것도 추가할 필요 없다. (어쨌건 인터페이스는 필요하다)
  2. 앞서 TEST_FRAMEWORK의 경우와 같이 TEST_IDCARD에서도 외부 억세스를 위한 인터페이스를 만들어야 한다.
    이 Package에 속한 ZCL_IDCARD와 ZCL_IDCARD_FACTORY를 인터페이스에 추가하면 자동적으로 그 부모 class의 object도 추가가 된다.

20100512_package_3

여기까지.
써놓고 보니 어지러워보이긴 하지만 개념 자체는 단순하다. 그래도 Java의 Package 컨셉이 훨씬 깔끔한 건 사실이다.

The Adapter Pattern (in ABAP)

(HFDP에서)
Duck의 object가 부족해서 Turkey의 object를 이용하려고 한다… 라는 이상한 상황을 가정하고 있다. 보통 기존 모듈이 새로운 모듈을 이용하려고 하나 서로 interface가 맞지 않을때 – 구체적으로, 한 클라이언트가 다른 object의 method를 이용하고 싶지만 기존 보유한 method를 바꾸고 싶진 않다(또는 바꿀 수 없다) 할 때… 같은 있음직한 상황을 예로 든다만.

어쨌건 이 경우 Duck은 client, Turkey는 Adaptee이다. TurkeyAdapter는 타겟 interface인 Duck를 구현하며 – 당연하다. Adapter니까 – 이때 Adaptee의 method들을 이용한다는 것이 포인트이다.

[text]
REPORT zp_adapter.

*PARAMETERS:

CLASS demo DEFINITION.

PUBLIC SECTION.
CLASS-METHODS: main,
test_duck IMPORTING i_duck TYPE REF TO zif_duck.

ENDCLASS. "demo DEFINITION

*———————————————————————-*
* CLASS demo IMPLEMENTATION
*———————————————————————-*
*
*———————————————————————-*
CLASS demo IMPLEMENTATION.

METHOD main.
DATA: gr_mallard_duck TYPE REF TO zcl_mallard_duck,
gr_wild_turkey TYPE REF TO zcl_wild_turkey,
gr_turkey_adapter TYPE REF TO zif_duck. " Adaptor for Duck
" i.e. Duck is the target interface

CREATE OBJECT gr_mallard_duck.
CREATE OBJECT gr_wild_turkey.
CREATE OBJECT gr_turkey_adapter TYPE zcl_turkey_adapter
EXPORTING
i_turkey = gr_wild_turkey.

WRITE: / ‘The Turkey says…’.
gr_wild_turkey->zif_turkey~gobble( ).
gr_wild_turkey->zif_turkey~fly( ).

SKIP 2.

WRITE: / ‘The Duck says…’.
test_duck( gr_mallard_duck ).

SKIP 2.

WRITE: / ‘The Turkey Adapter says…’.
test_duck( gr_turkey_adapter ).

ENDMETHOD. "demo

METHOD test_duck.
i_duck->quack( ).
i_duck->fly( ).
ENDMETHOD. "test_duck

ENDCLASS. "demo IMPLEMENTATION

START-OF-SELECTION.
demo=>main( ).
[/text]

test_duck은 Duck interface를 인자로 받는다. Adapter는 Duck interface를 구현한 것이므로 test_duck의 인자로 Adapter를 넘길 수 있고, 그 Adapter의 method들 안에서 Adaptee의 method가 호출된다.

Duck, Turkey, TurkeyAdapter 등의 Class 및 Interface 선언 및 구현은 그냥 시스템 내 Global Class를 이용하였다.
그래서, 여기서는 생략한다.

Inner Join, Outer Join, FOR ALL ENTRIES IN

Inner Join, Outer Join, 그리고 독특한 Open SQL 문법인 FOR ALL ENTRIES IN 까지.
정리가 잘 안 되어서(특히 performance 입장에서) 여러 문서를 찾아다녀 보았다.
아래는 그에 대한 간단한 정리.

  • database view는 inner join을 이용한다. 즉 database view는 모든 table에 공통적으로 있는 records만 제공한다.
  • help views와 maintenance views는 outer join을 이용한다.
  • INNER JOIN 과 FOR ALL ENTRIES IN 중 어떤 것이 낫다고 단정짓기 어렵다. 보통 FOR ALL ENTRIES IN 을 추천하지만 경우에 따라서 INNER JOIN 이 나은 성능을 보여주기도 하기 때문이다.
  • FOR ALL ENTRIES IN 은 ‘outer join’이다… 라기보다, 이 문법을 통해 outer join을 구현할 수 있다 – 고 이해해야 한다.
    단 주의할 점이 몇가지 있다.
    1. FOR ALL ENTRIES IN 을 사용할 땐 Select 하는 Field들이 모여서 자동으로 키 값이 되어 중복되는 값을 모두 배제해버린다. (즉 SELECT * FROM … 과 SELECT COL1 COL2 COL3 FROM … 의 결과가 틀려질 수 있다) 따라서  반드시 원래 테이블의 키 값들을 SELECT 다음에 다 써주는 것이 좋다.
    2. If the “one” table (the table that appears in the clause FOR ALL ENTRIES IN) is empty, all rows in the “many” table (the table that appears in the SELECT INTO clause) are selected. Therefore make sure you check that the “one” table has rows before issuing a select with the “FOR ALL ENTRIES IN” clause.
    3. If the table on which the For All ENTRIES IN clause is based is very large, the performance will go down. Hence attempt should be made to keep the table size to a moderate level.
  • SELECT … FOR ALL ENTRIES IN <itab> WHERE <cond> 의 로직은 다음과 같다.
    If you specify a field of the internal table <itab> as an operand in a condition, you address all lines of the internal table. The comparison is then performed for each line of the internal table.
    For each line, the system selects the lines from the database table that satisfy the condition. The result set of the SELECT statement is the union of the individual selections for each line of the internal table.
    Duplicate lines are automatically eliminated from the result set. If <itab> is empty, the addition FOR ALL ENTRIES is disregarded, and all entries are read.

TIOBE Programming Community Index

TIOBE Programming Community Index 가기

어떤 Programming Language가 가장 인기있는가를 보여주는 index 입니다.

제가 매일 사용하는 ABAP의 경우 순위가 가장 많이 떨어졌네요. 20위.
얼마전 시작한 Haskell의 순위는 36위. 헐…

하지만 저에겐 언제나 흥미로운 언어인 C++와 Python은 각각 3위와 6위를 차지하고 있네요.
all-time-favorite인 C는 부동의 2위를 차지하고 있고.