Builder pattern의 유용한 용법 - Constructor overroding 줄이기.

Essay/Software 2016.06.03 21:48

외부에 service를 제공하는 모듈 (예를 들면, library)일 경우, 잘못된 사용을 원천적으로 막는 것은 상당히 어려우면서도 중요하다.(모듈의 사용성과도 밀접한 관계가 있다.)

그런데, module의 instance가 생성되고 나서, 초기화 작업 이후에는 다시 setting될 필요가 없는 값들의 경우, 되도록이면, final (혹은 const)로 선언하는 것이, 코드의 이해도를 높이기도 좋고, 잘못된 사용을 막기도 좋다.


이때, default argument 값을 사용할 수 있는 언어의 경우(C++, Python 등)는 별 문제 없는데, 그렇지 않는 경우(ex, Java)는 사용성을 위해서 module constructor를 overloading해야 한다.(대부분의 경우, default 변수가 사용될 경우, 굳이 매번 이 값들을 argument로 전달해야 하는 것은 상당히 번거롭다.)


예를 들면,  'name', 'numArms'와 'numLegs' 세개의 final 변수를 가지는 'Person' module의 경우


class Person {
    private final String mName;
    private final int mNumArms;
    private final int mNumLegs;

    public Person(String name, int numArms, int numLegs) {
        mName = name;
        mNumArms = numArms;
        mNumLegs = numLegs;
    }
    public Person(String name, int numArms) {
        this(name, numArms, 2);
    }
    public Person(String name) {
        this(name, 2, 2); // 2를 default value로 사용. 대부분의 사람은 팔 다리가 2개... 
    }
}

와 같이 작성된다.

그나마 argument가 3개인 경우가 저정도고, argument가 많아지면, 사용성을 높이기 위해서 overloading되는 생성자도 많아진다.

이 문제를 builder pattern으로 해결할 수 있다.

예를 들면.

class PersonBuilder <T extends PersonBuilder > { // Generic type T 를 사용하는 이유는, 이 Builder를 상속받는 builder를 위함이다.
    private final String mName;  // name은 default가 없으므로 생성시 외부에서 반드시 argument로 받아야 한다.
    private int mNumArms = 2;  // 대부분의 사람은 팔, 다리가 2개...
    private int mNumLegs = 2;

    public PersonBuilder(String name) {
        mName = name;
    }
    public T
    setNumArms(int numArms) {
        mNumArms = numArms;
        return (T)this;
    }
    public T
    setNumLegs(int numLegs) {
        mNumLegs = numLegs;
        return (T)this;
    }

    public Person
    create() {
        return new Person(mName, mNumArms, mNumLegs);
    }
}


그렇지만, 역시 해당 class를 상속받는 경우, 긴 argument를 가진 생성자를 상속해야 하는 불편함은 여전하지만, 이 문제 역시, builder를 상속받는 방법으로 해결할 수 있다.


class OldPerson extends Person { ...}

class OldPersonBuilder<T extends OldPersonBuilder> extends PersonBuilder<T> {
    public OldPersonBuilder
    create() { ...}
}


Generic을 사용했기 때문에, 아래와 같은 용법이 가능하다.

OldPersonBuilder<OldPersonBuilder> bldr = new OldPersonBuilder<OldPersonBuilder>("MyName");
bldr.setNumArms(2).setNumLegs(2);

대략 정리해 보면, 이와 같은 pattern을 사용할때는


Module에서 final. Default값 존재 X => Builder에서도 final

Module에서 final. Default값 존재 O => Builder에서 NOT final.


이정도일려나?


신고
Trackback 0 : Comment 0

Linux re-parenting. And child-subreaper.

Domain/Linux 2016.04.20 22:38
[ related information ]
* prctl
* See linux kerne for details ("kernel/exit.c : find_new_reaper())

------ commit(kernel/git/torvalds/linux.git) -----
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ebec18a6d3aa1e7d84aab16225e87fd25170ec2b

author	  Lennart Poettering <lennart@poettering.net>    2012-03-23 22:01:54 (GMT)
committer Linus Torvalds <torvalds@linux-foundation.org> 2012-03-23 23:58:32 (GMT)
commit	  ebec18a6d3aa1e7d84aab16225e87fd25170ec2b (patch)




=====================================================================
[ test code ]
-------------

$ cat b.sh
function echosleep2 {
    sleep 10
    echo 'sleep2 end'
}


function echosleep {
    echosleep2&
    sleep 5
    echo 'sleep end'
}

echo "hello: $BASHPID $$"
echosleep&
sleep 2
echo end
=====================================================================




=====================================================================
[common]
--------

  PID  PPID  PGID   SID CMD
 3241  2573  3241  3241 init --user
 ...
 4392  3241  3374  3374 gnome-terminal
 ...
 9882  4392  9882  9882 bash
 ...
=====================================================================



=====================================================================
[ < 2 seconds ]
---------------

  PID  PPID  PGID   SID CMD
33513  9882 33513  9882 bash            # ./b.sh (*a)
33514 33513 33513  9882 bash            # ./b.sh:echosleep() (*b)
33515 33513 33513  9882 sleep 2
33516 33514 33513  9882 bash            # ./b.sh:echosleep():echosleep2() (*c)
33517 33514 33513  9882 sleep 5
33518 33516 33513  9882 sleep 10
33519 15742 33519 15742 ps -e -o pid,ppid,pgid,sid,cmd
-------------

(*a): Bash terminal executed 'b.sh' as new process group leader.
      All child/grandchild processes are executed in the same process group.
=====================================================================




=====================================================================
[ < 5 seconds ]
---------------

  PID  PPID  PGID   SID CMD
33514  3241 33513  9882 bash            # ./b.sh:echosleep() (*b)
33516 33514 33513  9882 bash            # ./b.sh:echosleep():echosleep2() (*c)
33517 33514 33513  9882 sleep 5
33518 33516 33513  9882 sleep 10
33520 15742 33520 15742 ps -e -o pid,ppid,pgid,sid,cmd
-------------

(*b): Process group leader(*a) is disappeared.
      And there is no threads in this process.
      (If there is other threads in this process, it will become new reaper)
      So, it is re-parented to orphan-reaper("init --user").
=====================================================================



=====================================================================
[ < 10 seconds ]
----------------

  PID  PPID  PGID   SID CMD
33516  3241 33513  9882 bash            # ./b.sh:echosleep():echosleep2() (*c)
33518 33516 33513  9882 sleep 10
33522 15742 33522 15742 ps -e -o pid,ppid,pgid,sid,cmd
-------------

(*c): Same with above(*b).
=====================================================================



=====================================================================
[ > 10 seconds ]
----------------

  PID  PPID  PGID   SID CMD
33524 15742 33524 15742 ps -e -o pid,ppid,pgid,sid,cmd
-------------

=====================================================================


Sample code to get every SIGCHLD from descendents.
---------------------------------------------------


#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <errno.h>

void handle_signal(int signal) {
        pid_t pid;
        printf("Handler SIGCHLD: %d\n", signal);
        if (0 > (pid = wait(NULL))) {
		printf("Wait fails: errno: %s", strerror(errno));
	} else {
		printf("Wait done: %d\n", pid);
	}
}

int main() {
        struct sigaction sa;
	pid_t pid;

        // Print pid, so that we can send signals from other shells
        printf("My pid is: %d\n", getpid());

        // Setup the sighub handler
        sa.sa_handler = &handle_signal;

        // Intercept SIGHUP and SIGINT
        if (sigaction(SIGCHLD, &sa, NULL) == -1) {
                perror("Error: cannot handle SIGCHLD"); // Should not happen
        }

        if (0 > prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0)) {
                perror("Error: prctl");
        }

	pid = fork();
	if (pid) {
		int secs = 10;
		// parent
		printf("\nSleeping for 10 second\n");
		while (secs > 0)
			secs = sleep(10);
		printf("Parent DONE\n");
	} else {
		// child
		if (0 > execlp("bash", "bash", "b.sh", NULL)) {
			printf("execl fails: %s\n", strerror(errno));
		}
	}
}




신고
Trackback 0 : Comment 0

[Ninja] Feature of build-tool : Implicit dependency to input '.ninja' file

Domain/Android 2016.03.04 21:41

'Ninja' has implicit dependency on input '.ninja' file.

Let's thing about following example.


< build.ninja >


rule hello

     command = echo ${msg}


build aaaa: hello

      msg = aaaa


build build.ninja: hello

      msg = build.ninja


-----


$ ninja yyyyyyy

[1/1] echo build.ninja

build.ninja

ninja: error: unknown target 'yyyyyyy'



---------------



Ninja try to build input '.ninja' file if rule for the '.ninja' is defined.

And then Ninja try to build given target.


신고
Trackback 1 : Comment 0
◀ PREV : [1] : [2] : [3] : [4] : [5] : ... [99] : NEXT ▶

티스토리 툴바