디자인패턴에 대해서 관심이 많았으니 특별한 동기가 없어 공부를 하지 않았습니다. 여전히 동기는 없지만 좀더 강한 압박을 위해 이 공간에 강좌를 올리겠습니다. 내용은 아래 있는 참고자료를 통해 공부하면서 예제를 모두 C#으로 바꿔볼 생각입니다. 저 역시 공부하면서 올리는 것이므로 고수님들의 많은 조언과 격려 부탁드립니다. 감사합니다.
----------------------------------------------------------------------------------
1.1 Singleton 디자인 패턴 클래스에서 인스턴스를 정확히 하나만 생성하는 것을 보장해야 하는 경우 사용한다. 이렇게 생성한 인스턴스는 연결관리가 필요한 커넥션이나 프린터 출력 등에서 사용이 가능하다. 개인적으로는 어플리케이션 전반에 적용되는 셋팅 옵션등에서도 효과를 나타낼 수 있을 것으로 판단된다. 아래 C#을 이용한 예제를 첨부할 것이고 간단한 설명은 첨부의 주석으로 처리하도록 하겠다.
-- 예제 -- /* static 멤버인 m_Instance는 객체의 단일 공유 인스턴스이다. 이 단일 공유 인스턴스는 공개함수인 GetInstance()에 의해 참조값이 반환된다. GetInstance는 m_Instace가 null인 경우에만 m_Instance를 생성하므로서 단일성을 유지한다. 생성자는 Private로 만들어 숨겨져 있다.
m_TimeOfBirth는 단지 테스트를 위한 멤버이다. 이것은 이 인스턴스가 생성된 시간을 가지고 있다. */ /// <summary> /// 유일성을 유지하고 싶은 클래스 /// </summary> public class MySingleton { private static MySingleton m_Instanace = null; // 쓰레드로부터 안전한 인스턴스 생성을 위해 Mutex사용 private static System.Threading.Mutex m_Mutex = new System.Threading.Mutex();
#region 예제를 위한 부분 실사용중에는 별로 필요치 않음 private static string m_TimeOfBirth = "";
public string TimeOfBirth { get { return m_TimeOfBirth; } } #endregion
/// <summary> /// 생성자 Private으로 존재한다. 필요하지 않은것은 아니며 단지 숨겨져 있을 뿐이다. /// </summary> private MySingleton() { System.Console.WriteLine("Singleton 생성시간 = " + System.DateTime.Now.ToLongTimeString()); m_TimeOfBirth = System.DateTime.Now.ToLongTimeString(); }
/// <summary> /// 인스턴스를 리턴하는 함수이며 인스턴스가 null인 경우에만 인스턴스를 생성한다. /// </summary> /// <returns>MySingleton 인스턴스 (this.m_Instance)</returns> public static MySingleton GetInstance() { m_Mutex.WaitOne();
if (m_Instanace == null) { m_Instanace = new MySingleton(); }
m_Mutex.ReleaseMutex();
return m_Instanace; } }
/// <summary> /// MySingleton을 실행하기 위한 클래스 /// </summary> public class Code { static void Main() { MySingleton objTest1 = null; //첫번째 인스턴스 MySingleton objTest2 = null; //두번째 인스턴스 int i = 0;
//첫번째 object에 MySingleton 인스턴스를 전달하고 시간을 출력해 본다. objTest1 = MySingleton.GetInstance(); System.Console.WriteLine("인스턴스 호출 = " + System.DateTime.Now.ToLongTimeString() + ";" + "objTest1.TimeOfBirth() = " + objTest1.TimeOfBirth);
//시간 지연을 위한 코드 *본인PC에서는 약 2초의 지연이 생김* for(i = 1 ; i < 1500000000; i++) { }
//두번째 object에 MySingleton 인스턴스를 전달하고 시간을 출력해 본다. objTest2 = MySingleton.GetInstance(); System.Console.WriteLine("인스턴스 호출 = " + System.DateTime.Now.ToLongTimeString() + ";" + "objTest2.TimeOfBirth() = " + objTest2.TimeOfBirth);
//두개의 object의 생성시간이 같은것을 볼 수 있다.
System.Console.ReadLine(); } }
********************************************* 쓴사람 김종령 참고자료 Professional VB.NET 디자인패턴 (정보문화사) - 위 참고자료는 예제기준이 VB입니다. 이것을 C#코드로 변경하고 내용을 간단하게 줄였습니다. ********************************************* 출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1053&page=22 |
성공을 위한 경쟁력 기르기 [10계명]
성공하고 싶은가? 성공적으로 다재다능한 IT 전문가가 되고 싶다면 지금 어떤 노력을 하고 있는가? 그렇다면 기술과 지식을 지속적으로 확대하는 것이 지름길이다. 아래 목록은 당신의 이력을 발전시키는데 필요한 핵심 경쟁력들을 보여준다.
IT전문가로서의 성공적인 이력은 언제나 도전이 따르며 그 이유는 모든 것에 능통하려고 노력하기 때문에 전문분야에 정통하지 못하게 되기 때문이다. 어쨌든 다방면의 지속으로 확대되는 크로스-펑션(cross functional) 경쟁력을 갖추어야 최고가 될 수 있다. 아래 목록에서 가장 놀랄만한 것은 다음과 같다. 대부분의 경쟁력은 전통적인 IT 기술을 초월하며 다른 직종에도 적용될 수 있다. 즉 통상적으로 생각되는 것 보다 IT전문가와 다른 분야 전문가 사이의 차이점은 적다.
1. 기존 기술과 신기술을 이해하라 모든 IT 전문가들이 필요로 하는 근본적인 경쟁력은 전문 기술에 관한 깊고 넓은 지식이다. 마슬로우의 필요 계층구조에 비유하자면 음식과 물에 해당한다. 즉 단순한 생존을 위해 꼭 필요한 것이다. 수업을 듣고 글을 읽으며 제품을 연구하고 전문가 집단에 가입하고 테크리퍼블릭에서 더 많은 시간을 보내야 하지만 당신이 사용하고 있는 기술에 대한 모든 정보가 있는지 확실히 하고 이를 적용하기 위한 베스트 프랙티스도 확보하고 있는지 살펴본다.
자격증을 원한다면 목표가 더 많은 타이틀에 있는 것이 아니라 교육 경험의 가치를 취대화하는데 있다는 점을 확실히 하자. 승자가 되기 위해서는 현재 상황에 주목하는 것과 동시에 미래 동향에 대해서도 잘 알아야 하기 때문이다. 역사적 증거에 의하면 모든 시스템의 평균 수명은 약 18개월이다. 따라서 지금 막 완성한 시스템을 어떻게 다른 시스템으로 대처할 지 계획하는 과정은 바로 시작되어야 한다. 계획단계는 당신이 시스템에 대해 잘 안다면 매우 효과적이다. 새로운 경향에 대해 잘 아는 것이 기본적인 책무이며 IT 전문가라면 매일 해야 하는 일이다.
2. 기술적인 아키텍처 설계에 관심을 가져라 누구나 각 기능이 훌륭하게 고안되고 실천되는 시스템 컴포넌트를 구축할 수 있다. 그러나 더 큰 시스템의 일부가 되었을 때 삐걱거린다면 별로 성공적이라고 할 수 없다. 전체적인 애플리케이션과 네트워크 디자인에 책임을 지고 있거나 기업 아키텍처 지원을 위한 컴포넌트 구축 침의 일부이거나에 상관없이 좋고 탄탄한 아키텍쳐 설계 원칙에 대해 잘 알고 있어야 한다.
효과적인 기술 아키텍처의 설계는 사용편의성과 비용을 희생하기 않고도 시스템이 잘 동작할 수 있도록 구성요소들이 결합되도록 한다. 나는 아키텍처 설계가 오컴의 면도날에 기반했을 때 가장 성공적이란 사실을 발견했다. 오컴의 면도날이란 라틴어에서 유래된 표현으로 "개체는 필요도 없이 복제되서는 안된다"라는 의미를 갖는다. 다시 말해 단순한 것이 좋다. 설계에 대해 생각할 때 모든 조직이 독특한 과정을 보유하고 있지만 대부분의 과정은 매우 보편적이며 설정이 가능한 범용 솔루션으로 해결될 수 있다는 점을 상기하라. 많은 아키텍처는 더 나은 구성요소를 만드는 것이 아니라 비교적 소수의 기존 컴포넌트를 구매하고 조립함으로써 가능하다. 그렇게 되면 고객에게 더 짧은 시간에 더 적은 비용으로 품질이 좋고 사용이 쉬운 제품을 제공할 수 있다. 동일한 개념은 개별 애플리케이션과 시스템의 설께 및 개발에 동일하게 적용된다.
3. 시스템 통합을 통한 생산성 향상을 고려하라. 기술은 다방면에 기여를 하며 중요 기능 중에 우선순위가 높은 것은 과정의 자동화에 있다. 물품주문, 재고관리, 시장출시에 관련된 기존 방식을 사용하는 대신 공급망처리 기법을 사용하면 공급자와 생산자가 복잡한 사오작용을 통제할 수 있도록 함으로써 전체 과정이 효율화 된다.
이는 원료를 생산공정으로 통과시키고 고객의 손에 완제품을 제공하는 일련의 과정이다. 레거시 시스템으로 점철된 아키텍처를 보유한 조직은 이런 애플리케이션을 통해 데이터를 더 잘 이동시킴으로써 생산성을 향상시킬 수 있다. 샤베인-옥슬리 법을 비롯한 범률을 준수하려면 기업들이 내부 통제를 인증받아야 하며 이는 시스템간 보편적인 경계에서 자주 발견된다. 제품과 플랫폼이 확산될수록 그리고 기업들이 자체 시스템을 다른 시스템과 더 많이 연결할 수록 고품질 호환성은 필수불가결하다.
4. 사업 관행, 접근법, 조직, 정치, 문화의 이해는 기본! 기업의 개체들은 복잡한 생물로 눈의 결정구조와 같아서 똑 같은 것이 하나도 없다. 특정 기업의 운영방식을 결정하는 동인은 쉽게 이해될 수 없다. 특히 대기업에서 여러 가지 문화가 존재하게 되는데 기업 수준의 문화와 사업부 문화, 부서 문화가 그것이다. 그리고 이제 안전하다고 생각한 순간 합병이나 경영진의 고체로 모든 것이 흔들리며 당신이 이해하고 있는 기업 운영의 직관이 배신당하게 된다. 정치를 무시하는 것은 스스로를 위험에 빠뜨리는 것이다.
타조 관리자를 싫어할 수 있을 것이다. 그러나 조류의 변화나 바람의 변화에 전혀 주의를 기울이지 않고도 기업의 변화 사이를 뚫을 수 있다고 자신할 수 없다. 마찬가지로 기업의 동인은 일반적이며 전달이 가능한 지식이기는 하지만 특정 기업 환경에서의 성공이 다른 기업 환경에서의 성공을 담보한다고 볼 수 없다. 따라서 우리는 각자가 처한 환경의 특성을 배워야만 한다.
5. 프로젝트관리 : 계획, 우선순위, 관리. 조 토레는 가장 훌륭한 야구 감독의 하나로 여겨진다. 조 토레가 훌륭한 계획을 갖고 있지 않았다면 1995년 이래 뉴욕 양키스가 10번 중 9번의 1위, 여섯 번의 AL 챔피온, 4번의 월드 시리즈 챔피온을 달성할 수 있었을 지 의심스럽다. 매년 포스트 시즌에 어떻게 진출하고 이기느냐에 관한 계획 뿐만 아니라 매 게임에 대한 계획 말이다.
관리자이거나 실무자이거나, 슈퍼스타이거나 평범한 사람이거나에 관계없이 단기적, 장기적 계획이 있어야 한다. 오늘 할 일은 ? 이번 주? 올해? 어떻게 계획을 달성할 수 있을까? "무엇"과 "어떻게"로 시작하는 질문을 자주하라. 개발자이거나 네트워크 관리자이면서 언젠가는 관리자가 될 생각이 있다면 이러한 계획 능력을 지금부터 개발해야만 한다. 스스로를 계획할 수 없다면 사람이나 복잡한 개체를 성공적으로 관리하는데 어려움을 겪을 것이다.
6. 의사소통과 듣기 : 정보수집 다른 일에는 평범한 수준이라도 의사소통에는 완벽해야 한다. 누구나 가져야 하는 핵심 경쟁력 두 가지 중의 하나로 IT전문가에게는 특히 중요하다. 좋은 의사소통은 양방향이며 주기와 받기 모두를 포함한다. 당신의 관대한 정신을 활용할 수 있는 훌륭한 분야인데 의사소통 과다란 없기 때문이다. 당신의 생각이 무엇이건 간에 모든 IT전문가는 실제로는 컨설턴트이다. 컨설턴트이기 대문에 고객에게 최대의 가치를 제공해야 할 책무가 있다. 이렇게 하기 위해서는 최소한 고객이 알고 있는 만큼 고객의 사업에 대해 알아야만 하며 이를 위해서는 잘 들어야만 한다. 당신의 고객들은 그들이 지불하는 돈에 대한 결과를 알 권리가 있으며 당신은 그들을 위해 무엇을 해주고 있는지 미리미리 알려주어야 한다.
IT전문가에게 이는 쉬운 일이 아니라. 우리가 IT직종을 선택한 이유는 사람보다는 소프트웨어와 하드웨어를 다루는 것이 더 편했기 때문이다. 그리고 대부분의 관용적인 사람들이다. 거절하기를 싫어하며 나쁜 소식을 전하기도 싫어한다. 그저 책상에 앉아 일하기를 좋아하는 경우가 많다. 이는 모두 치명적인 실수이며 전혀 쉽지 않고 성격에 반한다고 할 지라도 이러한 기술을 배우는 것 외에 대안은 없다. 좋은 뉴스도 있다. 누구라도 배울 수 있으며 시간과 연습으로 더 쉬워질 수 있다.
7. 결과에 집중하라. 또 다른 주요 경쟁력은 실행력이다. 계획은 훌륭한 것이지만 말은 가치가 별로 없다. 결국 노력의 결과를 보여야 하낟. 좋은 출발점은 고객에 대한 사실을 아는 것이다. 이들이 누구이며 무엇을 원하는가? 컨설팅을 하는 IT전문가의 입장에서 당신의 지식과 경험에 기반해 고객에게 조언을 해야 할 의무가 있다. 그러나 결국 고객 ( 상관, 동료, 팀 리더, 혹은 당신의 일의 최종 고객) 이 결정을 내리며 이런 결정은 당신의 권고와 다를 있다는 점을 잊어선 안 된다. 자존심을 포기하고 합의된 성과창출에 필요한 모든 일을 하라. 분석 마비증으로 스스로를 속박하지 말고 자의적인 수준의 완벽함을 위해 돈키호테식의 십자군이 되지도 말라. 80-20규칙을 적용하라. 결과의 80%는 20%의 노력으로 달성되며 그 수준을 넘어서는 가치는 대부분 투입비용에 미치치 못한다.
8. 전략적이로 사고하라. 경쟁이 심화되는 세계이며 현재의 IT전문가들은 그들이 매일 전술적, 전략적인 가치를 더하고 있으며 회사에서 열리는 모든 회의에서 환영 받는다는 것을 증명해야만 한다. 지난 10여 년간 기업은 IT의 전략적 중요성을 인식하기 시작했으며 IT가 단순히 회계부서의 뒷방 역할이 아니라 기업전체에 가지를 더한다는 점도 이해하기 시작했다. IT 전문가들은 서비스 제공업자들이며 우리는 스스로를 그렇게 이해해야 한다.
당신 회사의 비즈니스와 전략적 계획에 친숙해 지고 이런 계획을 지지하고 개선할 수 있는 방안을 찾으려고 계속 노력하라. 당신의 기업에 이런 계획이 없는가? 기술을 위해 하나를 만들라. 최소한 당신의 부서는 전력적으로 운영될 것이며 이를 발판으로 활용하여 회사의 비즈니스를 지원할 수 있도록 계획을 확대하는데 있어서 리더십을 제공할 수 있을 것이다.
대부분의 IT 부서들은 수동적이며 고객이 새로운 시스템에 대한 아이디어를 갖다 주기를 바란다. 잘 작동하고 성공적인 IT 부서들은 능동적이며 고객들과 컨설팅 방식으로 협력해서 일하고 이는 전체적인 회사의 목표와 목적 달성을 위한 것이다.
9. 영향력과 설득력을 길러라 1950년대의 군대식 명령하달식 조직 모델은 보다 수평적인 구조로 바뀌었다. 나는 아직 상사들은 분명히 있고 아직 직접 보고를 한다는 사실을 알고 있다. 하지만 당신의 성과를 측정하는 사람들이 당신에게 일을 주는 사람과 동일할 필요는 없다. 여기에 일부 지리적 분산이 추가되며 분산화 경향도 있으며 독자적인 조직들도 있는 것이 현재 기업 조직이다.
직접경영은 영향력에 의한 경영으로 바뀌었다. 더 이상 사람들에게 일을 시키지 않고 그것을 할 수 있도록 영향력을 행사한다. 확신을 시킨다. 협상을 하고 속이기도 하며 다그친다. 의사소통을 기억하는가? 당신이 개발해 온 의사소통 방식을 여기서 활용할 수 있다. 가장 중요한 경쟁력의 하나로 전략적 사고와 성과 지향중의를 가져오기 때문이다. 영향력 행사를 잘하는 IT전문가들은 향상 효과적이고 경쟁ㅈ거이며 존경을 받게 된다. 영향력 행사가 관리자들만을 위한 요소라고 잘 못 생각해서는 안 된다. 영향력과 설득력은 협력적인 직무 환경을 만드는 핵심 기술이기 때문이다.
10. 적응력을 갖춰라. 기술 전문가의 의미가 특정 개발 환경에 전문지식이 있거나 네트워크를 구축하고 지원할 수 있는 기술을 가졌다는 것을 의미하던 시절은 갔다. 오해는 하지 말라. 아직도 위와 같은 지식으로 돈을 잘 벌 수 있고 IT서비스 제공의 입장에서 당신도 다른 사람들처럼 전문가이다. 그러나 진정으로 다재다능한 IT전문가가 되려면 기술을 확대하고 다듬는 일을 계속적으로 해야만한다.
기술적 능력이나 지식과 같은 일부 경쟁력은 비교적 쉽게 얻어진다. 사업 시직과 같은 다른 경쟁력은 더 많은 시간이 걸린다. 개인과 팀의 관리, 리더십, 동료와 고객과의 협업 능력은 개인의 인성과 개성에 기반한 행동적 경쟁력을 요구한다.
IT 분야에서 일을 한다면 그 정의에 따라 CA(agent of change)가 되기로 한 것이다. 우리의 작업은 빠르게 그리고 근본적으로 변화하며 이에 적응하는 것은 우리의 책무이다. 좋건 싫건 간에 비즈니스는 변하기 때문이다. 경쟁적인 압력, 새로운 산업 진입, 경영진 교체, 전략적 이동, 제품 개발, 다른 요소들이 변화를 만든다. 또한 기술에 의해 영향을 받지 않는 부서는 그 어느 회사에도 없으며 책임감 있는 전문가들인 우리는 이러한 변화에 기업이 적응할 수 있도록 선도해야만 한다.
출처 : http://blog.naver.com/oyukihana/60024902882 |
3. String과 StringBuilder 비교
string은 프로그램을 짜면서 가장 빈번하게 사용되는 자료형의 하나이다. string을 쓰는데 있어서 흔히들 string 연산이 많을 경우 string 타입 대신에 StringBuilder 객체를 쓰라는 얘기를 들어 보았을 것이다. 여기에서는 string과 StringBuilder의 차이에 대해 다루며 각각을 어떻게 이용하는 것이 효율적인지에 대해 다루어보도록 하겠다.
먼저 아래의 간단한 프로그램을 보도록 하자.
class Program { static void Main(string[] args) { string str1 = "" StringBuilder strBuilder = new StringBuilder();
// string object Console.WriteLine("\nString Object\n"); for (int i = 0; i < 17; i++) { str1 = str1 + "A" Console.WriteLine(GC.GetTotalMemory(true).ToString()); }
// StringBuilder object Console.WriteLine("\nStringBuilder Object\n"); for (int i = 0; i < 17; i++) { strBuilder.Append("A"); Console.WriteLine(GC.GetTotalMemory(true).ToString()); } } } |
위의 예제 소스는 간단히 프로그램의 메모리 사용량을 비교해보기 위해 만들어본 소스이다. 단순히 증가되는 프로그램 영역의 메모리를 카운트하는 용도로 GetTotalMemory 라는 메서드를 이용해서 비교해 보았다. 이제 결과를 보기로 하자.
그림. 실행결과
string
실행결과 그림에서 보이는 바와 같이 string 으로 선언된 str1 변수의 경우 17번 반복하는 동안 연속적인 메모리 증가를 보인다. 실제 string의 내부적인 구현은 char 타입의 연속적인 열의 형태로 구성되는데, 선언시 초기화되는 데이터의 값으로 string 타입 변수의 크기가 결정된다고 보면 된다. 위의 소스와 같이 “+”와 같은 결합 연산자를 이용하여 경우 데이터의 크기가 증가하므로 할당되는 메모리 역시 증가되어야 한다.
이제 코드를 상세히 보도록 하자. 위의 소스에서 다룬 스트링 연산에서,
str1 = str1+"A"; |
부분을 디스어셈블러를 통해 추적해 보면 Concat(string, string)를 호출하는 것을 볼수 있다.
IL_0062: call string [mscorlib]System.String::Concat(string, string) |
실제 Concat 라는 메서드를 한번 보기로 하자.
public static string Concat(string str0, string str1) { if (string.IsNullOrEmpty(str0)) { if (string.IsNullOrEmpty(str1)) { return string.Empty; } return str1; } if (string.IsNullOrEmpty(str1)) { return str0; } int num1 = str0.Length; string text1 = string.FastAllocateString(num1 + str1.Length); string.FillStringChecked(text1, 0, str0); string.FillStringChecked(text1, num1, str1); return text1; } |
소스에서 눈여겨 봐야 할 부분은 FastAllocateString이라는 메서드 이다. 여기에서 보이는 것과 같이 새로 메모리를 할당하는 것을 볼 수 있다. 실제 Concat 메서드를 통해 볼 수 있듯이 새로운 문자열과 결합해서 문자열을 생성해내야 하는 문자열 연산은, 연산이 이루어질때마다 새로운 string 개체를 생성하고 스택 내 메모리를 재할당하는 과정을 반복하게 된다. 문자열에 대해 증가에 대해서 “A"라는 문자를 증가시킬 때 인덱스가 2씩 증가할 때 마다 메모리가 증가되는 이유는 char 형이 실제 2바이트를 이용하지만 할당되는 메모리 단위기 4바이트씩 증가되기 때문에 실행결과 그림과 같이 보인다.
string 타입의 제약은 메모리 재할당 문제로 인해 발생한다. 메모리의 안전한 보호와 동적인 수정이 가능한 구조로 가기 위해 실제 string 타입이 선택한 방법은 수정요구시 마다, 새로운 string 타입을 생성하고 적절한 사이즈로 메모리를 할당하고 문자열을 배정하는 것이라고 보면 된다.
StringBuilder
처음 나온 예제소스에서 보면 17번 반복해서 문자열을 한자씩 더해가고 있다. 그 이유는 StringBuilder 개체를 생성할 때, 초기에 그 크기를 선언하지 않으면 16으로 크기가 자동으로 결정된는 것을 보여주기 위해서 문자열의 크기를 맞추어 보았다. 출력 결과를 보면 “A"라는 문자가 16개가 된 후 StringBuilder 개체 역시 메모리 재 할당 과정을 수행하게 된다. 따라서 테스트용으로 작성된 소스의 17번째 반복 시점에서 메모리 사용이 증가하는 것을 발견할 수 있게 된다.
메모리 재할당이 이루어지는 단계에서는 새로운 StringBuilder 개체 생성과 기존 크기의 2배 크기로 메모리 할당, 기존 데이터의 복사가 이루어진다.
자 이제, StringBuilder 개체 생성시 적절한 사이즈를 설정한 후 작업을 하게 되면 재할당 과정에 드는 리소스의 사용을 최소화 할 수 있다는 것을 알 수 있을 것이다.
StringBuilder에 대한 좀더 상세한 내용은 다음에 더 다루어 보도록 하겠다.
결론
string과 StringBuilder는 비슷한 용도로 활용되면서, 많은 차이점을 가지고 있다. 일반적으로 얼마되지 않는 스트링 연산과 데이터를 이용하는데 있어서 string이 성능상 더 나을 수도 있다. 그럼 StringBuilder을 왜 사용하는 것일까?
아래의 간단한 성능 테스트 코드를 보자.
class Program { static void Main(string[] args) { StringBuilder strBuilder = new StringBuilder(100000); string str1 = ""
// 시간 측정을 위한 코드 Stopwatch stopWatch1 = new Stopwatch(); Stopwatch stopWatch2 = new Stopwatch(); TimeSpan time1, time2;
// string object Console.WriteLine("\nString Object");
stopWatch1.Start(); for (int i = 0; i < 100000; i++) { str1 = str1 + "A" } stopWatch1.Stop();
time1 = stopWatch1.Elapsed; Console.WriteLine("string 반복시간 : " + time1.Milliseconds.ToString());
// StringBuilder object Console.WriteLine("\nStringBuilder Object"); stopWatch2.Start(); for (int i = 0; i < 100000; i++) { strBuilder.Append("A"); } stopWatch2.Stop();
time2 = stopWatch2.Elapsed; Console.WriteLine("StringBuilder 경과 시간 : " + time2.Milliseconds.ToString()); } } |
위의 소스는 시간 측정을 하기 위해 System.Diagonstics 의 Stopwatch 클래스를 이용하는 코드와 결과를 명확히 하기 위해 반복 횟수를 100000으로 조절하여 테스트 한 소스이다. 변수 할당같은 경우 소요시간이 엄청 짧기 때문에 반복횟수를 크게 했다. 결과는 아래와 같다.
그림. 실행결과
동일한 100000번의 반복에서 경과시간을 비교해볼 수 있을 것이다. 이렇듯 많은 연산이 요구되는 경우 string은 성능상에 많은 문제를 일으킬 수 있다는 것을 보여주고 싶었다.
많은 연산이 수행되는 경우 효과적인 성능을 보장할 수 있기에 StringBuilder 클래스를 이용해서 작업을 수행하는 것이다. 적절한 크기를 명시하고, 적절한 장소에서 이용된다면 편리하고 시스템에 덜 부담을 주는 코드를 만들어 낼 수 있게 된다.
적당한 것 예를 들어, 데이터베이스 컨넥션 스트링이나 기타 문자열 결합 형식 등을 통한 일반적인 몇 자 안되는 문자열 생성 등에는 string 타입 변수 선언을 해서 이용하는 것이 이득이고, 대규모의 문자열에서 잦은 수정과 반복을 통한 추가 등이 이루어져야 한다면 StringBuilder을 쓰는게 좋을 수 있다.
리플들
안녕하세요.
글은 잘 읽었습니다. 그리고 궁금한게 있어서요..
만약 StringBuilder의 크기를 주지않고 한번에 더하는 텍스트의 길이가 16이상인 텍스트를 10번 더하는 경우라면, string과 StringBuilder가 같은 성능을 보이게 되는 것인지요?
아~~ 늦게서야 확인했습니다.
요즘은 데브피아에 잘 못들어와서.ㅡ.ㅜ
StringBuilder의 경우, 선언된 크기보다 큰 문자열이 들어올 경우 재할당을 하게되는데 크기의 증가는 배수로 증가하는 로직을 탑니다.^^;
처음에 16, 다음에 32, 64, 128.... 이런식으로요. 경우에 다라서 다르지만 String보다는 여전히 StringBuilder이 유리하죠.^^;
String의 경우 간단하게 보면 10번 개체가 생성되어야 하지만, StringBuilder의 경우 5번 생성된다고 보시면 됩니다.ㅎㅎㅎ
출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1229&page=15
인터페이스의 구현에는 두 가지가 있습니다. 이른바 암시적 구현과 명시적 구현이라고 하는데요, 일반적인 경우에는 주로 암시적 구현을 사용하지만 경우에 따라서는 명시적 구현을 사용해야 하는 경우도 있습니다.
먼저 암시적 구현 부터 살펴볼까요? 두 개의 인터페이스가 있다고 합시다.
01 public interface IGunner
02 {
03 void Shoot();<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
04 }
05
06 public interface ISoccerPlayer
07 {
08 void Shoot();
09 }
코드 1
IGunner는 총을 쏘는(shoot)을 메소드를 정의하고 있고, ISoccerPlayer는 공을 차는(shoot) 메서드를 정의하고 있습니다. 공교롭게 메서드의 이름이 같긴 하지만, 이 두 인터페이스는 서로 전혀 연관이 없습니다. 또한 3번과 8번 라인에서 아무런 접근 지정자가 붙어있지 않은 점도 유심히 보시기 바랍니다. 클래스의 경우 멤버의 접근 지정자가 붙어있지 않다면 private이 생략된 것으로 간주하지만, 인터페이스의 경우에는 public이 생략된 것으로 간주합니다. 아니 간주하는 것이 아니라, 인터페이스의 멤버는 그 성격상public외의 접근 지정자를 가질 수가 없습니다. 따라서 항상 public이기 때문에 인터페이스의 멤버에는 아무런 접근 지정자를 붙일 필요가 없습니다. 오히려 접근 지정자를 붙이면 문법 오류입니다.
이번에는 이들 인터페이스를 각각 구현하는 총잡이와 축구 선수 클래스를 생각해봅시다.
01 internal class Gunner : IGunner
02 {
03 public void Shoot()
04 {
05 Console.WriteLine("빵빵");
06 }
07 }
08
09 internal class SoccerPlayer : ISoccerPlayer
10 {
11 public void Shoot()
12 {
13 Console.WriteLine("슛");
14 }
15 }
코드 2
인터페이스에 정의된 메서드의 이름이 같기 때문에 이를 구현하는 두 클래스에서의 메서드 이름도 같을 수 밖에 없지만, 그 동작은 전혀 다릅니다. 총잡이는 총을 쏘고, 축구 선수는 공을 차지요. 그리고 3번 라인과 11번 라인에서는 public 접근 지정자가 붙어 있습니다. 인터페이스를 정의할 때는 붙이지 않았던 접근 지정자를 인터페이스를 구현하는 클래스에서는 이렇게 지정하여야 합니다. 인터페이스에서의 접근 지정자가 (생략된) public 이었기 때문에, 클래스에서도 public 이외의 다른 값을 가질 수는 없습니다. (곧 살펴 볼 명시적 인터페이스에는 적용되지 않는 이야기입니다..)
총잡이와 축구 선수의 인스턴스를 만들고 각각 Shoot 메서드를 호출해보도록 합시다.
1 private static void Main(string[] args)
2 {
3 Gunner gunner = new Gunner();
4 gunner.Shoot();
5
6 SoccerPlayer soccerPlayer = new SoccerPlayer();
7 soccerPlayer.Shoot();
8 }
코드 3
빵빵
슛
계속하려면 아무 키나 누르십시오 . . .
당연히 의도한 대로의 결과가 나왔습니다.
여기까지가 인터페이스의 암시적 구현에 관한 이야기였습니다. 지금부터는 인터페이스의 명시적 구현에 관한 내용입니다.
명시적 구현을 설명하기 위해 새로운 클래스를 하나 등장 시켜야겠습니다. 군인 클래스인데요. 군인이니까 당연히 총을 쏩니다(shoot). 그리고 군인이니까 당연히(?) 공도 잘 찹니다(shoot). 그래서 병사 클래스는 IGunner와 ISoccerPlayer를 모두 상속 구현하도록 결정을 하겠습니다.
01 internal class Soldier : IGunner, ISoccerPlayer
02 {
03 public void Shoot()
04 {
05 Console.WriteLine("빵빵");
06 }
07
08 public void Shoot()
09 {
10 Console.WriteLine("슛");
11 }
12 }
코드 4
한 눈에 봐도 에러입니다. Shoot 메서드가 중복이 되었지요. 위와 아래의 메서드는 각각 Gunner와 SoccerPlayer의 Shoot인데, 이를 구별하지를 못합니다. 즉 시그니처(메서드의 형을 구별하는 기준, 쉽게 말하면 메서드의 이름과 매개 변수의 개수와 각 매개 변수의 형)가 동일한 메서드를 가진 두 인터페이스를 동시에 구현 상속하지 못하는 상황을 해결하기 위해 C#의 설계자는 별도의 문법을 고안하였습니다. 그것이 바로 인터페이스의 명시적 구현입니다.
인터페이스를 명시적으로 구현하는 문법은 간단합니다. 구현하는 메서드 이름 앞에 누구의 메서드인지만 표시해주면 됩니다. 다음 코드는 코드 4를 명시적 구현으로 바꾼 코드 입니다.
01 internal class Soldier : IGunner, ISoccerPlayer
02 {
03 void IGunner.Shoot()
04 {
05 Console.WriteLine("빵빵");
06 }
07
08 void ISoccerPlayer.Shoot()
09 {
10 Console.WriteLine("슛");
11 }
12 }
코드 5
3번 라인과 8번 라인을 보면 Shoot 메서드 앞에 각각 인터페이스의 이름을 붙이고 있습니다. 또한 접근 지정자가 사라졌습니다. 접근 지정자가 없기 때문에 Shoot은 private 멤버입니다. 이 부분에 대해서는 잠시 후 다시 말씀 드리겠습니다.
이제 군인이 총을 쏘고 공을 차는 코드를 보겠습니다.
1 private static void Main(string[] args)
2 {
3 Soldier soldier = new Soldier();
4 soldier.Shoot(); // 총 쏘기
5 soldier.Shoot(); // 공 차기
6 }
코드 6
코드를 작성하긴 했는데 영 기분이 이상합니다. 아니나 다를까 문법 에러라서 컴파일도 되지 않습니다. 코드 5의 Shoot 메서드는 Soldier의 private 메서드라서 접근할 수 없다고 합니다. 혹 접근이 된다고 하더라도, 4번 라인에서는 총을 쏘고 5번 라인에서는 공을 차야 하는데, 뭐가 무엇을 하라는 말인지 구별이 안 갑니다. 결론적으로 인터페이스가 명시적으로 구현이 되면 Soldier 변수를 통해서는 접근할 수가 없다는 사실을 알았습니다. 그렇다면 이 군인이 총을 쏘고 공을 차게 하는 방법은 무엇일까요?
답은 Soldier가 아닌 IGunner 혹은 ISoccerPlayer를 통해서 접근을 한다는 것입니다. 즉 총을 쏠 때는 Soldier 객체를 IGunner로 형변환 하고, 공을 찰 때는 ISoccerPlayer로 형변환 한다는 것입니다.
1 private static void Main(string[] args)
2 {
3 Soldier soldier = new Soldier();
4
5 ((IGunner)soldier).Shoot(); // 총 쏘기
6 ((ISoccerPlayer)soldier).Shoot(); // 공 차기
7 }
코드 7
형변환을 하지 않는다면 Soldier 객체를 만들어 IGunner 혹은 ISoccerPlayer 변수가 그를 참조하도록 하는 방법도 있습니다.
1 private static void Main(string[] args)
2 {
3 IGunner gunner = new Soldier();
4 gunner.Shoot(); // 총 쏘기
5
6 ISoccerPlayer soccerPlayer = new Soldier();
7 soccerPlayer.Shoot(); // 공 차기
8 }
코드 8
이 경우에는 예컨데 3번 라인과 같이 생성된 Soldier 객체를 IGunner 변수에 참조시키면 IGunner 변수를 통해서는 총만 쏘지 공을 찰 수는 없습니다.
이제 코드 5를 다시 보며 두 Shoot 메서드의 접근 지정자에 대해서 생각을 해 봅시다. 코드 6에서 확인하듯이 Soldier 클래스의 입장에서 두 Shoot 메서더는 private 입니다. 하지만 코드 7에서 보듯이 IGunner와 ISoccerPlayer의 입장에서는 각각의 Shoot 메서드는 public 입니다. 결국 명시적으로 구현된 인터페이스 메서드의 접근 지정자는 상황에 따라서 서로 다른 두 개의 접근 지정자를 가지게 되는 것입니다.
이제 인터페이스의 암시적, 명시적 구현을 모두 다루었으니, 응용으로 이 둘을 섞어 놓은 형태를 보도록 합시다. 다음 코드를 보시기 바랍니다.
01 internal class Soldier : IGunner, ISoccerPlayer
02 {
03 public void Shoot()
04 {
05 Console.WriteLine("빵빵");
06 }
07
08 void ISoccerPlayer.Shoot()
09 {
10 Console.WriteLine("슛");
11 }
12 }
코드 9
3번 라인은 암시적으로, 8번 라인은 명시적으로 인터페이스 멤버를 구현하고 있습니다. 8번 라인이야 ISoccerPlayer의 Shoot을 구현한다고 명확히 알 수 있겠는데, 3번 라인의 경우에는 명확히 알 수가 없습니다. 이 경우 컴파일러는 Soldier가 구현하여야 한는 Shoot는 두 개가 있는데 그 중 하나는 ISoccerPlayer의 Shoot이기 때문에(8번 라인) 나머지 하나는 IGunner의 Shoot로 인식을 합니다.(3번 라인)
이를 사용하는 코드는 다음과 같습니다.
01 private static void Main(string[] args)
02 {
03 Soldier soldier = new Soldier();
04
05 ((IGunner)soldier).Shoot(); // 공 차기
06 ((ISoccerPlayer)soldier).Shoot(); // 공 차기
07
08 soldier.Shoot(); // 총 쏘기
09 }
코드 10
코드 7과 달라진 점은 8번 라인과 같이 Soldier 변수를 통해서도 Shoot을 호출할 수 있다는 것입니다. 이 때 Soldier의 Shoot는 IGunner의 Shoot를 구현하고 있기 때문에 결국 8번 라인은 5번 라인과 동일한 결과가 나타납니다.
빵빵
슛
빵빵
계속하려면 아무 키나 누르십시오 . . .
인터페이스의 명시적 구현을 마무리 짓기 전에 한 가지 중요한 사실이 있습니다. 실컷 명시적 구현을 연습해 놓고 이런 말 하려니 좀 허탈하긴 한데, 바로 인터페이스의 명시적 구현은 ‘꼭 필요한 곳이 아니면 가급적 사용하지 말라’는 것입니다. 그 이유는 여태껏 이야기한 내용에 들어 있습니다. 예를 들어 Soldier 클래스를 사용하는 개발자는 Soldier 클래스만 봐서는 Shoot 메서드의 존재를 알기 어렵습니다. 또한 만일 Soldier가 클래스가 아닌 구조체라면, Shoot메서드를 호출하기 위해 ((IGunner)soldier).Shoot과 같이 형변환을 할 때 마다 추가적인 박싱이 일어나기도 합니다.
출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1470&page=7
Visual Studio 활용 팁 1 탄
Visual Studio 활용 팁 2 탄
출처 - 1탄 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1495&page=6
출처 - 2탄 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1494&page=6