인자객체 표준 사용안

가변성이 요구되는 모든 메서드나 클래스는 결국 인자객체를 갖도록 전체 프레임웍을 조정 중입니다. 이러한 인자객체의 사용법에 혼동이 없도록 작은 규칙을 만들어 봅니다.

클래스 내의 인자객체 이용원칙

  • 하나의 클래스에 하나의 인자객체만 사용할 수 있다.
  • 인자객체는 직접 생성할 수 없다.
  • 인자객체를 갖는 클래스는 반드시 static변수를 통해 ARG 라는 이름으로 인자객체를 제공해야한다.

클래스 내 구현 샘플

static private var arg:CSArg = new CSArg( BScoreBlockNew.F );

static public function get ARG():CSArg{
	arg.clear();
	return arg;
}

public function CS( $arg:CSArg = null ){
	var param:Object;
	param = arg.data;
}

인자객체 작성원칙

  • 어떠한 클래스로부터도 상속받거나 의존하지 않는다.
  • internal var data:Object 를 반드시 제공한다.
  • internal function clear():void 를 반드시 제공한다.
  • 클래스의 이름은 반드시 인자객체를 이용할 클래스이름 + Arg 를 사용한다.

인자객체 구현 샘플

package core{

	final public class CSArg{

		internal var data:Object;

		public function CSArg( $blockNew:BScoreBlockNew ){
			if( $blockNew === null){
				throw Error( 'CSArg는 생성할 수 없습니다.');
			}
			clear();
		}
		internal function clear():void{
			data = {};
		}

		public function isSlave():CSArg{
			data.slave = true;
			return this;
		}
	}
}
Browser does not supports flash movie


관련된 글:

  1. CcircleStack
  2. CpvContainer의 속성처리 부분
  3. Cstack
  4. rest인자를 통한 인자객체의 대체
  5. 인자객체의 그 다음

35 Comments

  1. 지돌스타 says:

    var textField:TextField = CStext.FACTORY( CStext.ARG.embedFonts( false ).autoSizeNone().alignCenter().color( 0x0000ff).font( ‘dotum,AppleGothic’ ).size( 12 ).text(“지돌스타”) );
    addChild( textField );

    보내주신데로 해보니깐 너무 잘되네요.

    생소하지만 API Doc으로부터 자유해지고 IDE의 코드힌트를 적극활용할 수 있고 단 몇줄로 보통이면 객체하나를 생성하기 위해 10~20줄을 사용할 것을 1~2줄로 끝내버리는 장점이 있네요. {}를 이용할때보다 컴파일타임에서 제어되기 때문에 코딩 실수를 줄일 수 있고…. 이거 보면 볼 수록 매력적이군요.

  2. 지돌스타 says:

    제가 생각할때 Arg로 시작하는 클래스는 모두 internal로 지정해도 무방하지 않나요?
    어짜피 Arg클래스들은 지정된 패키지 외에서는 생성 자체가 무의미 하기 때문이라고 생각하기 때문입니다.

    • admin says:

      그렇게 되면 곤란한 점은 호스트코드에서 인자객체의 메서드를 호출할 수 없다는 점입니다. 존재의 의미 자체가 위험에 빠지죠. 좀 더 좋은 대안이 있으면 하는 바람이 있습니다.

  3. 지돌스타 says:

    음… 제가 테스트해본 결과 class자체가 internal이라고 하더라도 class내 메서드들이 public이면 다른 패키지에서 생성에 관여하는 것은 원천방지가 되면서 접근은 충분히 가능한 것으로 알고 있습니다.

    ArgText가 internal로 지정되도 그냥 아래처럼 사용해도 문제가 없다는 건데요.
    var textField:TextField = CStext.FACTORY( CStext.ARG.embedFonts( false ).autoSizeNone().alignCenter().color( 0×0000ff).font( ‘dotum,AppleGothic’ ).size( 12 ).text(“지돌스타”) );
    addChild( textField );

    위처럼 사용해도 ArgText는 CStext와 같은 패키지이므로 생성은 가능하지만 접근은 CStext를 통해서만 가능하므로 CStext.ARG…. 로는 문제없이 되고요.

    그렇다면 또 한가지 ArgText가 internal로 지정되어 버리기 때문에 blockNew를 사용할 필요도 없어지고요.

    • admin says:

      엇 그래용? ㅎㅎ 그럼 더욱 좋죠 ^^
      일반적인 언어에서는 그러한 점프가 불가능한게 원칙이라 as3로 그럴거라 예상했는데 아니군요 ㅎㅎ
      실험해봤습니다 성공!
      당신은 고마운 사람!!!
      p.s 그래도 new를 블럭하는건 여전히 좋아용 ^^ 그렇게 막지 않으면 다른 클래스가 패키지 내부에서 편법으로 쓰려고 할거에요(저의 분신이?) ㅎㅎ 완전한 격리 상태에 있다는건 여러가지로 도움이 되죠. 절대로 그것은 문제가 아니다 라고 확신할 수 있으니까요.

    • admin says:

      참 그리고 한가지 더 있어요.
      이 버전의 인자객체 안에는 포함되지 않았던 추가 스펙인데, 하나의 클래스가 여러개의 인자객체를 갖는 경우가 있어요. 대표적으로 CSgraphics의 경우 ArgGraphic이랑 ArgGradient 를 갖습니다.
      이 경우 기존의 방식에서는 CSgraphics.ARG 와 CSgraphics.GRADIENT 를 통해 참조하는데 이 방식을 버리고 xelf 프로젝트에서는 CSgraphisc.ARG.getGradient()….. 이란 방식으로 하나의 ARG만 소유하도록 바꾸고 나머지 인자는 인자객체가 다시 주도록 했습니다.
      하나의 클래스가 하나의 ARG만 관리하는게 맞는거 같아서요.
      display 패키지에도 곧 반영될 예정입니다.
      그렇게 되면 ArgGraphic안에 다음과 같은 메서드가 생기는 식입니다.

      public function getGradient():ArgGradient{
      return new ArgGradient();
      }

      인자객체가 this를 넘겨 재귀호출개념의 데코레이터로 작동한다면 중간에 그 대상을 바꾸는 것도 가능하다는거죠 ^^

  4. 지돌스타 says:

    ㅎㅎ 저도 도움이 되는군요.. ^^;

    한가지 코딩 규칙적 제안이라면…
    Internal로 이왕 만든다고 하면 클래스명 규칙에 한가지 포함하면 어떨까요?

    $XLAText 또는 _XLAText 뭐… 이런거요.
    이쁘기는 ‘_’ 가 더 좋아보이는데요.

    AIR 같은 경우에도 HTMLLoader로 로드되는 html문서의 dom을 __HTMLScriptObject로 표현하는데… 여기서도 _를 두번 쓴 것 같더군요.

    일단 패키지를 보면 internal과 public 클래스들이 있는데 겉모양으로봐선 구분이 잘 안가더라구요. 물론 만든사람은 익숙하면 상관없는데 처음 본사람은 까봐야 안다는… ^^

    • admin says:

      그게 사실은 인터널의 위한 이름규칙은 없는게 맞다라고 생각하는 부분이 있습니다.
      왜냐면 인터널 클래스는 결국 배포한 뒤엔 호스트측에서 볼 수가 없잖아요.

      하지만 의견을 주신걸 곰곰히 생각해보면 도입하는게 더 좋을 것 같습니다.
      만들어진 유틸리티를 그저 swc로 소비하는게 아니라 그 유틸리티 자체를 개선하고 사용하는 사람들에게는 확실히 인터널에 이름규칙이 있는편이 인지하기 쉽고 정리도 잘될듯합니다.

      참 최신 코딩 규칙은 여기에 정리되어있습니다. 이미 보셨을지도 모르겠지만 ^^;
      http://code.google.com/p/xelf/wiki/codeRule

      버전 1.2에 반영토록 해보겠습니다 ^^

  5. 지돌스타 says:

    제가 완전 도배하네요….

    xelf.googlecode.com에서 text부분 보고 있는데요. LOAD와 SAVE의 역할은 충분히 이해했어요. 이는 미리 설정값을 저장해뒀다가 textField를 생성시 설정하기 위한 것이고 LoadSET, SET부분은 이미 있는 textField를 재설정하기 위한 것이잖아요? (이걸 아는데도 시간 걸림 ㅡㅡ;)
    LOAD, SAVE는 문제가 없는 것 같습니다. 근데 LoadSET, SET부분엔 문제가 있던데… 이 부분은 아직 완성 안된건가요? 생각보다 크기가 커졌네요….

    • admin says:

      옙 아직 미완성입니다. 사실 코드가 미완인게 문제가 아니라 SET이라는 기능 자체를 정확하게 정의하지 않고 있는 상태입니다 ^^
      또한 숨겨진 버그인데 LOAD후에 인자객체를 통해 속성을 더 추가하면 해당 LOAD한 스타일이 변경되는 문제가 있습니다.
      예를들어

      arg.LOAD(‘test’).alignCenter().html(…)

      이렇게 하면 test라는 스타일에 가운데 정렬이 반영되어버려 이후에 test를 사용할때는 가운데 정렬도 적용되어버립니다.
      이걸 방지하려면 LOAD가 복사본을 리턴하도록 개선되어야합니다.

      팀원에게 시킬 예정 ㅎㅎ

  6. 지돌스타 says:

    답변 감사해용~

    클래스에 대한 이름 규칙에 있어서 생각을 해봤습니다. Static, Virtual이 CS, CV로도 할 수 있지만 어짜피 클래스라는거 다 아니깐… 그냥 S, V 로 해도 무방할 것 같다는 생각을 했어요.

    일반클래스 : C로 시작. 무조건 public. 기본 final
    추상클래스 : V로 시작. 항상 상속해야 쓸 수 있으며 public이다. final 아님
    정적클래스 : S로 시작. 예)Stext
    인터페이스 : I로 시작. 예)Itext
    내부클래스 : _로 시작. _S, _C등…final internal이 기본. 예) _Clink, _Sexpression
    인자클래스 : _A로 시작 final internal이다. 예)_Atext
    Part 클래스 : 클래스명_소문자로 시작하는 파트명. final internal이 기본. 예) 클래스가 Vs라면 Vs_key로…
    Block처리 클래스 : _BlockNew패키지명. 가령 _BlockNewCore

    제 생각에…. 패키지 kr.xelf.core, kr.xelf.display 그냥 이렇게 하지 않고 kr.xelf.XLcore, kr.xelf.XLdisplay 처럼 하는 것은 오히려 사용성에 어렵지 않을까 생각합니다.

    • admin says:

      그것에 대해서는 매우 중요한 점이 있습니다.
      일반 명사에 가까운 이름을 클래스 이름으로 사용하면 매우 안좋은 일이 일어납니다. 왜냐면 일반명사에 가까운 이름은 다른 패키지에서도 사용하고 있을 가능성이 농후하기 때문입니다. 특히 중요한 코어라이브러리들군이나 api가 점령하고 있을 가능성도 높습니다.

      import com.bside.display.display;
      import kr.co.xelf.display.display;

      var dis:display; //에러난다!

      var dis:com.bside.display.display; //풀네임으로 기술할 수 밖에 없다!

      이런 문제를 당하는 것이죠.

      따라서 일종의 이름 중복방지를 위한 시그니쳐입니다. 저의 경우엔 C, CS라는 이름 규칙을 bsidesoft 패키지에서만 사용하니까 그건 회사클래스라는 시그니처가 되고 xelf는 XL을 붙여서 시그니처를 만들어내죠.
      만약 시그니쳐의 도움이 없다면 순식간에 CStext가 두개가 되어서 호스트에서 같이 쓰려고 하면 낭패를 당했을것입니다.

      사실 이걸 처음 당한건 트윈맥스의 트위닝함수 이름이 fl패키지거랑 같아서 통합 트위너짤때 이러한 시그니처 도입을 절감했습니다 ^^

      추가적으로 인자객체를 internal로 하면 안되는 이유를 하나 설명드릴께요.

      인자객체의 힘은 단지 객체 생성 타임에 국한 되지 않아요.

      var arg:ArgTween;
      arg = CStween.ARG;
      CSloop.For( 1, 10, function( $key:int ):Boolean{
      arg.object( CSdoc.GET( view, $key.toString() ) ).target( {x:500 – $key*5} ).ease( ‘qdi’, 2, $key * delay );
      return true;
      } );
      CStween.SET( arg );

      위의 코드에서는 매우 손쉽게 10개의 트위닝 셋을 생성해냅니다. 이 경우 var arg:ArgTween; 여기서 public이 아니면 안되기 때문에 public이 필요하게 됩니다.
      확실하게 외부에서 사용할 필요가 없는 인자객체는 internal도 괜찮다고 생각합니다만 ^^

  7. 지돌스타 says:

    인자객체의 활용도 부분에서 저런식으로 할지는 생각을 못했네요.

    그럼 인자객체의 접근에 대해서 interface로 구축하면 어떨가요?
    public interface IAtween, final internal class _Atween 이런식으로 해서 여전히 인자객체 생성은 막으면서 참조는 가능하도록 하면 원래 인자객체를 사용하려는 의도를 따를 수 있지 않을까 생각합니다.

    • admin says:

      하지만 그것도 약간 말이 안되는 점은 인터페이스를 구상하는 클래스가 단 한개라는 점입니다.
      또한 인터페이스와 클래스가 완전히 같은 메서드를 갖고 있다는 점도 있습니다.
      따라서 추상층만 노출하는 의미가 전혀 없다고도 볼 수 있습니다 ^^
      만약 모든 인자객체가 특정 인터페이스를 구상하여 기본적인 구현을 같이 가져간다면 그것자체는 나쁜 아이디어는 아닙니다.

      대표적으로 제것에서는 clear()나 data 를 공통으로 가져가니까요.

      하지만 만약 이러한 인터페이스가 모든 인자객체 공용이라는 가정이 된다면 패키지간 의존성을 유발하게 됩니다. display를 알기 위해 core.Iarg를 알아야하는 사태가 되는거죠.
      만약 그렇지 않다면 결국 패키지마다 다른 IdisplayArg, IcoreArg 라는 식으로 알아야하는데, 이는 다시 말해 호스트가 그저 ArgDisplay와 ArgCore라는 구상클래스를 아는것과 별반 차이가 없습니다.

      하지만 여전히 좋은 아이디어를 짜내볼 부분이 많긴 합니다. 일단 현실적으로 가능한 범위만 커버한거라 ^^;

  8. 지돌스타 says:

    그렇네요. 인터페이스를 사용하는 범위를 제가 너무 축소시켜서 생각해버렸습니다. ^^

    설계라는 것이 완벽한 건 없는 것 같습니다. 그때그때 경험을 통해서 얻어져 약속을 해야하는 경우가 더 많네요. ^^

    • admin says:

      중요한 부분은 역시 플라톤이 말한대로 이데아를 찾아야지 모사된 형상에 집착하면 곤란하다는 것이겟죠 ^^
      추상층을 구현하는 목표는 객체통신 상의 서비스를 제공하는 측이 제공받는 클라이언트랑 맺는 계약 혹은 프로토콜이라고 보면 그것이 진짜 추상층으로 구현된 프로토콜이든 아니면 구상클래스 하나가 프로토콜이든 별로 상관없다고 봅니다(용호씨의 답변마저 풀어쓰기 할 필요는 없다고 생각해서 걍 막 올림 ^^)

  9. 지돌스타 says:

    한가지 궁금한게 생겼습니다.
    Flash Player API수준에서 이러한 방법을 사용하는데 제가 SimpleButton를 이용해서 TextField와 같은 방법으로 만들어보긴 했습니다. 물론 문제가 없었습니다. 단순히 ARG.normal(bitmapdata1).over(bitmapdata2).down(bitmapdata3).hit(bitmapdata) 정도로 만들어 FACTORY(ARG):SimpleButton 정도로 처리했거든요.

    하지만 ToggleButton같은 것을 만드려고 할때는 package격리수준에서 하려고 하니 문제가 생기더군요. 왜냐하면 FACTORY로 만들어 던져주는 넘이 flash player api 수준에서 던져주지 못하기 때문인데요.

    그래서 XLcomponent 패키지를 만들고 XLSbutton, XLStext 정도는 그냥 SimpleButton, TextField를 만들어주고(XLStext가 XLdisplay에서 이동된거죠.) ToggleButton의 경우 XLtoggle컴포넌트를 따로 만들어 XLStoggle, XLAtoggle 정도로 만들면 어떨까 생각했습니다. XLtoggle 컴포넌트는 XLStoggle.FACTORY()을 통해 객체가 반환되죠. 이렇게 되면 XLtoggle의 경우 직접사용할 수 있지만 기존 방식대로도 사용할 수 있기 때문에 문제는 없을 것 같은데요.

    CSdoc.ADD( stage,
    XLtoggle.FACTORY( XLtoggle.ARG.checked( checkedSimpleButton ).unchecked( uncheckedSimpleButton ) ),
    {k:’myToggle’,p:[0,0],ali:{click:hnClick}}
    );

    function hnClick($e:MouseEvent):void {
    var toggle:XLtoggle = XLtoggle($e.target);
    trace( toggle.checked ); //true or false
    }

    이정도 수준에서 쓸 수 있도록 하면 어떨까 합니다.

  10. admin says:

    음 처음엔 제가 말씀해주신걸 이해했다고 생각했는데 다시 읽어보니 이해하지 못하고 있는 상태인듯.
    그러니까 패키지란
    1. 패키지 내부의 클래스를 대표할 수 있어야하거나(추상화)
    2. 아니면 관련된 클래스를 묶어둔 단순한 집합의 이름일 수 있습니다(그룹화)

    위의 예가 만약 그룹화의 일환이라면 그 의미는 그저 개발자가 혼동하지 않게 비슷한 ui시리즈를 묶어두는 그룹핑 목적의 패키지를 도입하자는건가요,
    아니면 저 컴포넌트들이 상호간에 공통점이 있어 그들 모두를 제가 XLcomponent라 불러야하는 어떤 추상층이 필요하다는건가요.

    양쪽으로 사용가능에 대해서는 물론 XLcomponent가 Sprite를 구상하는 이상 무조건 달성됩니다만, 체크박스와 심플버튼과 텍스트에어리어 사이에 전혀 공통점을 찾지 못하고 있어서 제가 이해를 잘못하는 중 인듯.
    반대로 예상중이신 XLcomponent의 시그니처를 샘플로 보여주시면 한번에 이해할 수 있을듯합니다!!

  11. 지돌스타 says:

    저는 패키지의 역할을 이렇게 생각하고 있습니다.

    첫번째로는 패키지와 패키지 간에는 서로 의존성을 없애주는 것을 첫번째 목표로 합니다. (그래서 일부러 패키지 별로 라이브러리를 만들어 가시적 효과를 볼 수 있다고 생각하고 있습니다. 그 뿐아니라 서로 의존성이 없기 때문에 호스트코드를 만들어야하는 입장에서 다른 패키지가 필요없으면 쉽게 제외도 가능하겠지요.)
    두번째로는 말씀하신 그룹화를 통해 개발자가 특정목적의 접근을 쉽게 하자는 것입니다.
    세번째로는 첫번째가 실현하는 것이 곤란한 경우가 있으면 단방향 의존성을 가지게 한다 입니다. 가령 util정도가 되겠죠.
    (단 조건은 같은 라이브러리 안에서 이 조건을 씁니다.)

    첫번째, 두번째 조건에 벗어나지 않는다는 전제하에
    XLtext는 이미 XLdisplay의 다른 클래스와의 의존성이 없습니다.(그렇게 믿고 있습니다.)
    그리고 구현하고자 하는 XLtoggle과 XLbutton과 같은 ui 특성을 가지는 컴포넌트가 추가가 되는 이상
    이런 것들이 XLdisplay 패키지에 있으면 왠지 다른 클래스들과 연관성이 있어보입니다.
    그래서 따로 XLcomponent를 만들어 이쪽으로 몰아주자하는 취지입니다. 이것이 두번째 그룹화의 실현이겠죠.

    말씀하신 패키지 추상화에 대해서는 글쓰신 것중에 팩토리와 IPlayer를 예로 들으신 것으로 이해했습니다.
    즉, 호스트코드 입장에서 여러종류의 Player의 생성에 개입하지 않도록 해서 패키지 추상화를 실현하는 것이겠죠.

    ui컴포넌트는 그들 자신간에 의존성이 생길 수 있습니다. 가령 simplebutton과 togglebutton이 그 예가 될 수 있겠습니다.
    togglebutton은 simplebutton과의 단방향 의존성을 가집니다. 그러나 이건 결국 같은 패키지이미로 문제가 없습니다.
    또 한가지로 제가 list를 만들어야하는 상황이 된다면 XLlist를 만들어 인자객체에 layout 속성을 넣어
    Vertical, Horizontal, Tile등을 넣어 호스트 코드에서는 list의 추상층만 참고하도록 구성하면 또한 되겠다는 생각을 했습니다.
    이러한 취지에서 XLcomponent 패키지를 도입하면 좋겠다는 의견을 제시한 것이고 이 의견을 어떻게 생각하시는지 궁금했습니다.

    단지 제가 질문한 내용에서 문제가 된다고 생각하는 부분은 XLCtoggle을 만드는 것보다 Itoggle을 만들어야 패키지 추상화가 완벽하게 실현된다고 생각합니다.

  12. 지돌스타 says:

    위 세번째 조건은 실제로 고려대상이라고 생각하지 않습니다. 그런 상황은 있을 수 없고 있어서는 안되도록 만들어야 한다고 생각합니다.

    필요하다면 위 XLcomponent를 만들어서 블로깅을 하든지 해서 공유할께요.

    • admin says:

      프레임웍은 좋지만 셋트로 가져와야 쓸 수 있다는게 단점이기도 합니다.
      만약 XLcomponent 시리즈들이 단지 추상층을 갖는걸로 끝나지 않고 그들을 사용하기 위한 전용 컨테이너가 필요하다면 그것은 좋지 않겠죠. 마치 플렉스의 버튼만 업어오고 싶은데 전체 프레임웍을 가져오지 않으면 안되는 상황과 비슷합니다.

      하지만 반대로 체크박스나 라디오 버튼은 원래부터 이슈가 있다고 생각합니다. 라디오 버튼은 애시당초 그룹지어진 다른 라디오들과 통신을 해야하는데 이는 퍼사드를 통해 통제하는 컨트롤러가 없다면 개별 라디오마다 다른 모든 라디오의 레퍼런스를 알고 있어야하는 상황을 번지게 됩니다. 전 ui적인 니즈 때문에 XLcomponent는 필요하다는 생각에는 약간 회의적입니다만 이러한 모델이나 컨트롤러 층에서 컴포넌트추상층이 필요하다는것에는 오히려 공감합니다.
      배치나 정렬에 대해서는 짜피 그녀석이 컴포넌트가 아니라도 해결해야하는 모든 그래픽 요소의 주제이기 때문에 특별한 컨테이너를 구현하기 보다는 오히려 Sprite내에서 기능을 확장시키거나 그때그때 상황을 통제하는 함수 또는 도우미 객체가 적당하다고 생각합니다.
      실제로 스파크에서의 변화는 전략 패턴을 사용하도록 유도하고 있는데 더 넓은 범주에서보면 Sprite수준에서 전략객체가 onEnter에 개입하는게 더 타당하겠죠.

      해서 제 생각은 간단히 정리하자면 역활모델보다 더 기저의 의미로 구현할수록 유연성이 뛰어나고 의존성이 작아진다는 점입니다.
      즉 토글버튼으로 인식하는 수준과 Sprite로 인식하는 수준이라면 당연히 Sprite로 인식하여 기능구현한 부분이 더 유연하고 의존성도 작습니다.
      만약 이 경우 위에 예를 들어주신대로 API로 커버하지 못하는 경우라면 API로부터 상속을 받아서 구상객체를 만들어 전체적으로는 API로 인식할 수 있게 해주되 필요한 기능을 모은 추상층으로서 인터페이스를 도입하는게 좋지 않을까 라는 겁니다.

      그렇게 되면, 즉 만들어낸 객체는 특정 인터페이스를 통한 구상층을 구현하는데 이를 사용하는 측은 API수준의 추상층으로 해당 객체를 인식해버리면 – 호스트코드에서 다운캐스팅을 해야하는 상황이 발생합니다. 다운캐스팅을 안하면 에러가 안나니까, 컴파일타임엔 못막아도 런타임에 막으려고 하는거죠.

      OOP의 이론적인 상황에선 업캐스팅만 해야겠지만 의존성을 제거하다보면 다운캐스팅이 자주 발생합니다.
      바로 이점이 판단의 분기점인데, 전 생산성을 중시하잖아요 ^^
      그래서 모두 안전하게 업캐스팅구조로만 생산하지는 않습니다. 더 범용적으로 더 의존성없게 더 재활용할 수 있게에 더 무게를 두기 때문에 일부는 다운캐스팅을 통한 런타임에러로 넘기게 됩니다.

      P.S 마지막으로 XLdisplay…BSdisplay 라는 패키지는 실제적으로는 대부분 static으로 구성된 유틸 클래스 집단입니다. 일종의 거대한 핼프객체군이죠. 구상객체는 실제로는 프로젝트별로 생성되기 때문에, 그때그때 다른 편인듯합니다.
      구지 설명하자면 BSdisplay 내부에선 CStext뿐만 아니라 모든 클래스가 딱히 의존성이 없다고 봐야겠죠. 말씀하신 세번째의 조건, 즉 유틸로서의 기능만 공유할뿐입니다. 그래서 인스턴스를 리턴하지 않는 클래스가 대부분이고 만약 리턴하는 경우도 제가 좀 심하다 싶게 API수준으로만 리턴하게 유도를 하는 편입니다 ^^ 정확하게 이름을 짓자면 BS_flash_display_SupportSet 정도가 아닐까요 ㅎㅎ

  13. 지돌스타 says:

    결국 또 한번 선택의 문제이군요. 핼프객체군으로서 역할로만 봐야하는데 제가 생각하는 것은 이것과는 다른 관점에서 바라본거군요. ㅎㅎ 아무튼 답변 감사합니다. 충분히 제가 고민하고 생각한 것을 이미 하셨겠다고 생각해서 질문드렸어요.

    나중에 또 생기면 물어볼께요. ^^

    • admin says:

      그게 사실 충분히 다 고민했다고 볼 수는 없는게 실무상에서 이뤄진 프로젝트라 필요성을 느끼지 못한건 안만들어져있고 그러한 탓에 전체적으로 좋은 구조라기보다 필요한 녀석들만 정리한 상태에서 좋은 구조라고 할 수 있죠.
      따라서 여러가지 의견을 받아서 수정해야할 곳 투성이입니다.

      이번에 이 댓글대화를 통해서도 뷰를 구성하는 여러 요소들이 데이터 바인딩이나 퍼사드를 구현하기 위한 서포트 객체를 만들어야하는가라는 고민도 많이 했고, 현재 단순히 DisplayObject의 속성을 확정적으로 정의하는 것을 넘어 Hbox나 Vbox처럼 onEnter에서 지속적으로 반응하는 자식을 관리하는 컨테이너 알고리즘을 제공해주는 공급자를 만들까라는 생각도 많이 했습니다.

      더 광범위하게는 Hbox나 Vbox처럼 단순한 관리자 뿐만 아니라 box2d같은걸 이용해서 자식들의 자동 물리정렬, 스프링정렬 등의 정렬공급자를 확장할 수도 있을거같고, 스파크에서처럼 3D정렬공급자에서부터 일단 DisplayObjectContainer에게 공급할 수 있는 새로운 수준의 공급자들군을 인식하는 계기가 되었습니다.
      곧 DDO에 반영할 거 같아요 ^^; DDO 3.0 을 준비해야겠군요 ㅎㅎ

  14. 지돌스타 says:

    xelf의 XLtext에 대해서 항상 의문점을 가진것이 XLAtextSET의 존재였습니다.
    XLAtext의 LOAD내부에서 arg.LOAD(‘test’).alignCenter().html(…) 와 같이 사용할 경우 원본데이터가 수정되는 것을 방지하기 위해 LOAD마다 clone을 만들어야만 한다면 굳이 XLAtextSET은 필요없겠다는 생각이 들더라구요. 그냥 XLAtextSET은 버리고
    간단히
    XLtext.SAVE(‘name1′, Stext.ARG…. );
    XLtext.SET( textField, XLtext.LOAD( “name1″ ).text(“111″) );
    이렇게 사용해서 LOADSET 함수를 없애버리면 훨씬더 깔끔해보일것이라는 생각입니다.

    아.. 그리고 묻고 싶은 것이 ARG에 text, htmlText 함수에 String값이 들어갈때마다 문자열을 더해주도록 하셨는데…
    그보다 차라리 text, htmlText는 덮어 씌우는 것으로 하고 addText, addHtmlText를 추가적으로 만드는 것이 좋지 않을까요?

    • admin says:

      음 정말 SET은 제가 즉흥적으로 만든거라는 생각이 든다는.. 오히려 ARG.LOAD( ‘s1′, isClone=true) .alignCENTER() 라는게 훨씬 명확할거 같아요.
      단지 현재로서는 한번 정의한 스타일을 수정하는 구문이 명시적이지 않은데 그건 오히려 명시적인 스타일 구문으로 다른 메서드나 클래스를 정의해야할거같아요.
      CStext.editStyle( ‘s1′ ).alignCenter();
      CStext.removeStyle( ‘s1′ );
      이런게 더 좋을듯합니다.

      text와 html은 사실 동적으로 계속 내용을 갱신하는 일이 별로 없어서 그렇게 설계된거 같아요.
      add로 시작하면 코드힌트에서 잘 안보이니까
      text( string, isAdd = true )
      정도의 시그니처도 좋을듯합니다.

  15. 지돌스타 says:

    사실 이 부분에 있어서 필요하다고 느낀 것은 TextField의 각 속성이 수시로 바뀌는 경우가 있어서입니다. 가령 달력같은 것을 만든다고 가정한다면 중간중간에 글자의 색,크기 등이 언제든지 변할 수 있도록 하면 되는데 그때마다 TextField를 생성하는 것은 무의미 하다고 봤습니다. 아무튼 만드신 것 덕분에 제가 참 잘 애용하게 되었어요. 감사~

  16. 지돌스타 says:

    글쿤요… 그렇다면

    XLStext에는 아래 정도로 들어가주고…
    static public function SAVE($name:String, ARG:XLAtext):void {
    ARG.SAVE($name);
    }

    static public function LOAD($name:String, $isClone:Boolean = true):XLAtext {
    return ARG.LOAD($name, $isClone);
    }

    static public function EDIT($name:String):XLAtext {
    return ARG.LOAD($name);
    }

    static public function REMOVE($name:String):void {
    ARG.REMOVE($name);
    }

    인자객체에 아래처럼 들어가면 되겠네요.
    internal function EDIT($name:String):XLAtext {
    return LOAD($name, false);
    }

    internal function REMOVE($name:String):void {
    if(save[$name]) {
    delete save[$name];
    } else {
    throw new Error($name + ‘은 존재하지 않는 이름입니다.’);
    }
    }

  17. 지돌스타 says:

    앗…. 틀렸습니다. XLAtext에 EDIT는 아래처럼 되어야 겠네요.
    internal function EDIT($name:String):_Atext {
    if( save[$name] ) {
    _data = save[$name].data;
    _text = save[$name].text;
    } else {
    throw Error($name + ‘은 존재하지 않는 이름입니다.’);
    }
    return this;
    }

    • admin says:

      음 아니죠.

      LOAD, SAVE는 여전히 ARG의 메서드에 있어야하고
      EDIT는 CStext로 이사오는 것입니다.

      왜냐면 코드를 생각해보면

      //기존의 스타일을 편집한다.
      CStext.EDIT( ‘k1′ ).alignCenter();

      //스타일을 이용해 적용한다.
      CStext.FACTORY( CStext.ARG.LOAD( ‘k1′ ).html( ‘test’ ) );

      만약 SAVE, LOAD 를 CStext수준으로 이사가면..음

      //일단 로드한다.
      CStext.LOAD( ‘k1′ );

      //이후엔 그 스타일이 적용된다?
      CStext.FACTORY( CStext.ARG.html( ‘test’ ) );

      요런 애매한 상황이 되기 때문에 ^^;

  18. 지돌스타 says:

    하지만 제가 생각할때 CStext.ARG.LOAD() 형태로 가면 ARG로 접근할때마다 arg.clear()가 됩니다.
    결국 쓸데없는 객체생성을 해놓고 LOAD()해버리는것이니깐… 좋은 방법은 아니지 않을까요?

    그래서 CStext에서는
    function LOAD($name):void {
    //ARG.LOAD($name); -> 이게 아닌
    arg.LOAD($name); //-> 이거…
    }
    을 해야된다고 봅니다. 그래서 여전히 CStext에 LOAD가 있어야하는게 맞다고 생각해요.

    그리고 지적하신 마지막에 아래 부분 코드에서
    CStext.LOAD( ‘k1′ );
    CStext.FACTORY( CStext.ARG.html( ‘test’ ) );

    LOAD 자체에 대한 개념을 어떻게 잡느냐에 따라 문제가 될 수 있는데…
    ArgTest의 LOAD()는 clone을 만들어 던져줘야 CStext.FACTORY( CStext.LOAD(‘k1′).color(0xffffff) ); 처럼 사용했을때 기존 값이 바뀌어 오용할 수 있는 여지를 없앨 수 있다고 말했었습니다.
    그래서 ArgText의 LOAD()를 위처럼 사용되는 것 자체는 결국 무용지물이됩니다.
    결국 LOAD는 ArgText의 clone을 던져주고
    CStext.FACTORY( CStext.LOAD(‘k1′).html( ‘test’ ) );
    이런식으로밖에 사용할 수 없다는 겁니다.

    EDIT를 보면서 느낀건데요… 아래처럼 사용할 수도 있겠더군요.
    CStext.SET( textField, CStext.EDIT(‘k1′).background(0xffffff) );

    만약 저런 문구가 두번이상 들어가게 되면… 더 심각해지는게….
    CStext.SET( textField, CStext.EDIT(‘k1′).background(0xffffff) );
    CStext.SET( textField, CStext.EDIT(‘k1′).background(0xccffff) );
    EDIT로 넘어온 ArgText객체는 save내에 있는 것을 편집해서 넘겨주기 때문에 XLStext부분에서 구현된 SET함수의 delete data.defaultTextFormat 부분에서 처음 실행시 지워져버리고 다음에 또 같은 ArgText객체를 참조해버려서 지워져버린 data.defaultTextFormat음 참조하려다 보니 에러가 나버립니다.

    그래서 저런 경우에도 사용할 수 있도록 하려면
    XLStext.SET 함수가 수정되어야 겠더라구요.
    static public function SET($target:TextField, ARG:XLAText):TextField {
    var key:*, data:Object, format:TextFormat;
    data = ARG.getData();
    format = data.defaultTextFormat;
    //delete data.defaultTextFormat; ->이렇게 되면 EDIT를 오용할때 문제가 된다.
    for (key in data) {
    if( key === “defaultTextFormat” ) {} else {
    $target[key] = data[key];
    }
    }
    $target.defaultTextFormat = format;
    data = ARG.getText();
    for (key in data) {
    $target[key] = data[key];
    }
    return $target;
    }

    • admin says:

      음. 클래스란 원래 설계가 말해주는 부분이 있고 옹례가 말해주는 다른 정의들이 수반됩니다.
      예를들어 이 클래스는 반드시 인스턴스를 생성하고 open한뒤 send해야한다 라는 식이죠.
      CStext가 봉착한 문제도 같다라고 보여집니다.
      CStext를 사용하는 절차나 방법에 대해서 한없이 자유롭게 만들어주는건 그다지 의미가 없는 일이고 차라리 정해진 용례로 유도하는 편이 전체적인 오류를 막을 수 있습니다.
      따라서 스타일이란 부분이 원래 선언되고 사용되는게 중심이라면

      //스타일을 선언한다.
      CStext.ARG.size( 11 ).bold().SAVE( ‘style1′ );
      CStext.ARG.size( 15 ).SAVE( ‘style2′ );

      //스타일을 이용해 사용한다.
      CStext.FACTORY( CStext.ARG.LOAD( ‘style1′ ).html( ‘test’ ) );

      //사용하다가 스타일로부터 복사본을 살짝 변경하여 사용하곤 한다.
      CStext.FACTORY( CStext.ARG.LOAD( ‘style1′, true ).alignCenter().html( ‘test’ ) );

      //간혹 스타일 자체를 재정의하곤 한다.
      CStext.ARG.EDIT( ‘style1′ ).alignCenter();

      일단 위에서 언급하신 빈객체 생성문제는 메서드에서 지연생성을 통해 처리할 수 있습니다. 즉 if( data ) clear(); 가 각 메서드에 들어가면 처리될 내용입니다.
      또한 이 모든게 런타임에 이루어진다고 생각해보면 애시당초 미리 정의한 스타일을 저렇게 바꾸는게 자연스럽지는 않습니다. 오히려 기본이 되는 스타일을 만들어서 정의하고 그때그때 가감하여 쓰는게 정상이겠죠.

  19. 지돌스타 says:

    그렇군요.
    클래스 오용을 클래스 설계서부터 막을 필요가 있지 않을까 생각했습니다. 물론 적정한 선에서는 당연히 필요한것임니다만…
    하지만 현실적으로 생산성을 항상 고려하면서 만들어야하기 때문에 용례는 그에 적절한 대처 방안이 될 수 있겠네요.

    생성객체의 지연생성을 어떻게 적용할지는 좀 생각해봐야겠네요.
    사용스타일을 인지하기 쉽게 만들고 그로인해 불필요하게 정의된 것을 개선하는 편이 좋다는 말씀이시죠. 듣고 보니 공감합니다. ^^

    • admin says:

      오히려 덕분에 저도 이런저런 생각을 많이 해봤습니다.
      사실 만들면 문제가 생길때까지 방치하게 되다보니 부족한점이 많을듯.
      최근에 도입한 강력한 CStextARG의 기능은 @입니다. 이게 머냐면 ARG.html( ‘@안녕하세요’ ); 이렇게 @를 앞에 쓰면 인식하는 기능인데. @가 있으면 embedFont를 true로 해줍니다 ㅎㅎ
      이젠 이것없이 살수가 없다는.
      전제적인 구조 문제외에도 많은 좋은 기능들은 생각하기 나름이니 좋은 아이디어는 언제나 공유해요!

  20. 지돌스타 says:

    오 @는 참 좋은 것 같아요.
    생각한건데….
    CStext.ARG.EDIT( ’style1′ ).alignCenter(); 의 형태보다
    CStext.ARG.alignCenter().EDIT( ’style1′ ); 로 하는 것이 더욱 명확할 것 같습니다.

    EDIT가 void를 반환하므로
    CStext.SET( textField, CStext.ARG.color(0x0000ff).EDIT(“name1″) );
    이렇게 사용하는게 최소한 런타임상태에서 방지됩니다.

Leave a Reply