객체의 유니크 아이디 생성하기
글쓴이: shi510 / 작성시간: 금, 2018/07/06 - 3:13오후
문제의 코드
어떠한 객체를 인스턴스화 하였을 때 아이디의 유니크함을 보장해야하는 코드가 필요한데요.Object obj1, obj2; Print(obj1.Id()); Print(obj2.Id());
해결법 1
객체 내의 특정 자료형을 동적할당을 하여 해당 Memory Address를 유니크 아이디로 사용함.객체가 메모리에서 제거되었을 때 같은 Memory Address가 할당될 가능성이 있음.
유니크함이 컴파일러 또는 시스템 의존적임.
class Object{ public: Object(){ ptr_id = new int(0); } string Id(){ return to_string((int)ptr_id)); } ~Object(){ delete ptr_id; } private: int *ptr_id; };
해결법 2
static unsigned int 자료형을 선언하여 객체가 할당될 때마다 ID를 선형 증가하도록함.제 지식으로는 향후 문제의 여지가 있는 코드인지 모르겠습니다.
class Object{ public: Object(){ id = _next_gen; _next_gen += 1; } string Id(){ return to_string(id); } private: int id; static unsigned int _next_gen; }; unsigned int Object::_next_gen = 0;
방법2가 향후 문제가 없을까요?
아니면 다른 좋은 방법이 있을까요?
Forums:
해결법 2가 대체로 좋은 방법입니다만, 주의할 점이
해결법 2가 대체로 좋은 방법입니다만, 주의할 점이 좀 있겠군요.
1. Object 객체가 복사(copy)되거나 대입(assign)될 때 id도 똑같이 덮어씌워지지 않도록 주의해야 합니다.
(그런 면에서 볼 때, id만 따로 별도의 클래스로 만드는 게 편리할 것 같군요.)
2. 멀티스레드 환경에서 사용중이면, 혹은 멀티스레드 환경에서 사용될 가능성이 있으면
_next_gen에서 발생할 수 있는 data race를 막을 방법을 준비해야 합니다. lock을 두는 편이 제일 간단하지요.
답변 고맙습니다.
객체가 서로 다른 thread에서 생성되는 경우는 생각을 못했네요.
thread safety는 좀 더 생각해야 겠습니다.
고맙습니다~
...
사실 1번 방법을 쓴다면 별도로 int를 할당할 필요도 없고 그냥 객체 자체의 주소를 사용하면 됩니다. (물론 객체를 복사하면 주소가 바뀐다는 문제가 있습니다만... 유니크 아이디가 필요한 상황에선 복사 자체가 필요없는 경우도 많기 때문에...)
시간으로는 안 되나요?
시간으로는 안 되나요?
생성 시간을 기록하면 유일할 것 같아요.
cpu 사이클이 빨라서 동일 시간에 여러개 생성될 수도 있겠군요
2번 해결책이 일반적이고 보통 id++ 로 합니다.
atomic 연산해야 합니다. thread-safe
id = 0;
id = 0;
...
id = next_id++;
atomic 연산해야 되요.
객체의 역할을 분리시키세요.
Object 객체는 unique Id를 가지기만 하면 되지, 자기 자신이 생성할 필요가 없습니다.
만약 클러스터링 되는 환경, Server-Client 모델에서는 유니크 아이디를 어떻게 생성해야할까요?
따라서, 가장 일반적인 경우는 애초에 unique Id를 생성해주는 객체를 별도로 두는 설계입니다.
이럴 경우,
1) 단순하게 1씩 증가는 경우
2) DB와 연동하여 DB의 key을 객체의ID로 쓰는 경우
3) 클라이언트에서 서버에게 요청하여 Id값을 받은 후 할당하는 경우
모든 경우에 대해 유연하게 대처 할 수 있습니다.
즉, ID를 생성하는 객체만 교체가 된다면 기존의 Object 객체는 수정 없이 재활용 가능합니다.
...
음.. 관점의 문제지만, 저보고 하라면 "객체 안에 다 집어넣고, 클러스터링 되는 환경이 필요해지면 그때 refactoring합시다"라고 하겠습니다.
소프트웨어 프로젝트를 말아먹는 흔한 패턴 중의 하나가 "혹시라도 사용자가 백만명을 넘어가서 서버가 수백 대 필요해지면 어쩌구 저쩌구..."해서 그걸 다 감안해서 그 상황에서 돌아갈 수 있는 프로그램을 짜다 보면 소스는 몇십만 줄이 넘어가고... 나중에 출시 시기를 놓쳐서 폭망... 뭐 그런 경우들이 있죠.
(물론 반대로 백만명이 정말로 써야 하는데 서버 한 대에 우겨넣고 "제가 혼자 써보니까 잘 돌던데요?" 하면 그것도 문제겠습니다만...)
본문에 설명을 생략했더니 이렇게 되었군요.
본문에 설명을 생략했더니 이렇게 되었군요.
unique id generator를 인터페이스를 정의하고 그 구현체는 3가지 모두 구현 가능합니다. 이럴경우 모듈이 완벽하게 분리 되기때문에 다른 부분 수정없이 컴파일 및 링크가 되는 유연성있는 설계라고 말하고 싶었습니다. 단순하게 id 증분으로 코드 짜놓으면 10줄이면 되겠고, 인터페이스를 분리하였기에 추후에 리펙토링 필요하면 다른 모든 모듈에 영향을 끼치지 않겠지요.
결국 id생성자를 분리하는데 필요한 코드는 추기적으로 20줄 내외이고 언제 있을지 모르는 리펙토링에 조차 안전한 코드가 되는 겁니다.
c++ pimple idiom이 가장 간단한 얘가 되겠구요.
자바라면 interface로 정의했겠죠.
관련된 design pattern은 bridge pattern이 되겠습니다.
GUID, UUID 등도 참고해 보세요.
GUID, UUID 등도 참고해 보세요.
https://ko.wikipedia.org/wiki/%EC%A0%84%EC%97%AD_%EA%B3%A0%EC%9C%A0_%EC%8B%9D%EB%B3%84%EC%9E%90
https://charsyam.wordpress.com/2012/12/26/%EC%9E%85-%EA%B0%9C%EB%B0%9C-global-unique-object-id-%EC%83%9D%EC%84%B1-%EB%B0%A9%EB%B2%95%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A0%95%EB%A6%AC/
PYTHON의 UUID 모듈 (twinwings님의 설명대로 외부로 빠져있는 형태)
https://docs.python.org/ko/3.7/library/uuid.html
boost uuid 사용하기
https://stackoverflow.com/questions/3247861/example-of-uuid-generation-using-boost-in-c
Signature :) - "여유를 갖고 행동하되 게을러지지 말자"
댓글 달기