본문 바로가기

Language61

클래스와 인터페이스 상속보다는 컴포지션을 사용하라 상속(클래스가 다른 클래스를 확장하는)은 코드를 재사용하는 강력한 수단이지만, 항상 최선은 아니다. 패키지 경계를 넘어 다른 클래스의 구체 클래스를 상속하는 일은 위험하다. 메서드 호출과 달리 상속은 캡슐화를 깨드린다. 상위클래스가 어떻게 구현되는냐에 따라서 하위 클래스 동작에 이상이 생길 수 있다. 상위 클래스는 릴리즈마다 구현이 바뀔 수 있어 문서화를 해두고 발맞춰 수정해야만 한다. 아래 예제에서 addAll()이 c.size()를 더하고 있는데 add()메서드에서 한번 더 더하기 때문에 2배가 된다. 그렇다고 addCount를 더하는 부분을 지운다고 안심할 수 없다. 나중에 상위클래스(HashSet) 어떻게 바뀔지 모르기 때문이다. public class Instrum.. 2022. 6. 14.
클래스와 인터페이스 변경 가능성을 최소화하라 클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다. 불변 클래스는 장점이 많고, 단점은 특정 상황에서의 잠재적 성능 저하뿐이다. 이것을 불변 클래스라고 부르는데 인스턴스의 내부 값을 수정 할 수 없는 클래스이다. 생성된 시점의 상태를 파괴될때까지 가져간다. 불변 클래스의 규칙 1. 객채의 상태를 변경하는 메서드(변경자)를 제공하지 않는다. 2. 클래스를 확장 할 수 없도록 한다. - 하위 클래스에서 부주의하게 혹은 나쁜 의도로 객체의 상태를 만드는 사태를 막아준다. 3. 모든 필드를 final로 선언한다. 4. 모든 필드를 private으로 선언한다. - 외부에서 직접 접근하여 필드를 수정하는 것을 막아준다. public final로 사용한다면 다음 릴리즈때 내부 표현을 바꾸지.. 2022. 6. 12.
클래스와 인터페이스 클래스와 멤버의 접근 권한을 최소화하라 잘 설계된 컴포넌트는 클래스 내부 데이터와 구현 정보를 외부 컴포넌트에 얼마나 잘 숨겼느냐다. 정보를 은닉해서 구현부와 API를 분리시켜 오직 API로만 외부 컴포넌트와 소통하며 서로의 내부 동작 방식에는 전혀 개의치 않는다. 정보 은닉의 장점 1. 시스템 개발 속도를 높인다. 여러 컴포넌트를 병렬로 개발 할 수 있기 때문이다. 2. 시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨리 파악하여 디버깅 할 수 있고, 다른 컴포넌트로 교체하는 부담도 적기 때문이다. 3. 정보 은닉가 자체가 성능을 높여주지는 않지만, 성능 최적화에 도움을 준다. 완성된 시스템을 프로파일링해 최적화할 컴포넌트를 정한 다음, 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화 할 수 .. 2022. 6. 12.
모든 객체의 공통 메서드 Comparable을 구현할지 고려하라 Comparable interface를 통해 인스턴스들에 작연적인 순서를 만들 수 있다. compareTo는 단순 동치성과 순서를 비교 할 수 있다. 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable interface를 구현하자. public interface Comparable { public int compareTo(T o); } compareTo 메서드의 일반 규약 이 객체와 주어진 객체의 순서를 비교한다. · 나보다 작은 객체 음의 정수 · 나보다 같은 객체 0 · 나보다 큰 객체 양의 정수 · 비교 할 수 없는 경우 ClassCastException 규약 1. sgn(x.compareTo(y)) == -sgn(y.x.. 2022. 6. 11.
모든 객체의 공통 메서드 toString을 항상 재정의하라. toString을 잘 구현한 클래스는 사용하기에 훨씬 즐겁고, 그 클래스를 사용한 시스템은 디버깅하기 쉽다. 일반 규약 간결하면서 사람이 읽기 쉬운 형태의 유익한 정보를 반환하라. 모든 하위 클래스에서 이 메서드를 재정의하라. 실전에서 toString은 그 객체가 가진 주요 정보 모두를 반환하는 게 좋다. - 스스로를 완벽히 설명하는 문자열이어야 한다. eg. "맨해튼 거주자 전화번호부(총 1487536개)" 포맷을 명시하든 아니든 여러분의 의도는 명확히 밝혀야 한다. - 포맷을 명시하거나 주석을 작성하여 의도를 명백히 밝혀야 한다. /* * 이 전화번호의 문자열 표현을 반환한다. * 이 문자열은 "XXX-YYY-ZZZZ"형태의 12글자로 구성된다. * XXX는 지역코.. 2022. 6. 7.
모든 객체의 공통 메서드 equals를 재정의하려거든 hashCode도 재정의하라. equals를 재정의한 객체의 hashCode재정의 하지 않으면 HashMap, HashSet같은 컬렉션의 원소로 사용 할 때 문제를 일으킨다. hash 일반 규약 1. equals비교에 사용되는 정보가 변경되지 않았다면, 애플리케이션이 실행되는 동안 그 객체의 hashCode메서드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 한다. (단, 애플리케이션을 다시 실행한다면 이 값이 달라져도 상관없다.) 2. equals(Object)가 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 똑같은 값을 반환해야 한다. 3. equals(Object)가 두 객체를 다르다고 판단했더라도, 두 객체의 hashCode가 서로 다른 값을 반환할.. 2022. 6. 6.
모든 객체의 공통 메서드 INTRO Object는 객체를 만들 수 있는 구체 클래스지만 기본적으로는 상속해서 사용하도록 설계되었다. Object에서 final이 아닌 메서드(equals, hashCode, toString, clone, finalize)는 모두 재정의를 염두에 두고 설계된 것이라 재정의 시 지켜야 하는 일반 규약이 명확히 정의되어 있다.일반 규약에 맞게 재정의 하지 않으면 규약을 준수한다고 가정하는 클래스(HashMap, HashSet 등)를 오작동하게 만들 수 있다. equals는 일반 규약을 지켜 재정의하라. equals 메서드는 재정의하기 쉬워 보이지만 곳곳에 함정이 도사리고 있어서 자칫하면 끔찍한 결과를 초래한다. 재정의 하지 않으면 클래스의 인스턴스는 오직 자기 자신과만 같게 된다. 재정의 하지 않는 상.. 2022. 6. 6.
객체 생성과 파괴 finalizer와 cleaner 사용을 피하라. finalizer는 예측 할 수 없고, 상황에 따라 위험 할 수 있어 일반적으로는 불필요하다. cleaner는 finalizer보다 덜 위험하지만, 여전히 예측하기 어렵고 느리고 일반적으로는 불필요하다. 피해야 하는 이유 1. 언제 실행될지 알 수가 없어서 제때 실행되어야 하는 작업은 절대 할 수 없다. 2. 상태를 영구적으로 수정하는 작업에서는 절대 사용하면 안된다. DB lock해제를 finalizer나 cleaner에게 맡겨놓는다면 분산 시스템 전체가 서서히 멈출 것이다. 3. 심각한 성능 문제도 동반한다. 4. finalizer공격에 노출되어 심각한 보안 문제를 일으킬 수도 있다. 생성자나 직렬화 과정에서 예외가 발생하면, 이 생성되다 만 객체에서.. 2022. 6. 5.
[Java] 참조 참조란? 참조란 객체나 변수가 메모리를 생성하지 않고 기존에 존재하는 메모리에 접근하는 것이다. 참조의 종류 강한 참조(String Reference) Object o = new Object(); 강한 참조를 통해 참조되고 있는 객체는 가비지 컬렉션의 대상에서 제외된다. new로 생성된 객체, 네이티브 피어, Queue / Stack처럼 스스로 메모리를 관리하는 객체 등... GC의 대상이 되도록 한다. o = null; 소프트 참조(Weak Reference) MyClass ref = new MyClass(); SoftReference softRef = new SoftReference(ref); JVM의 메모리가 부족한 경우에만 힙영역에서 제거되고 메모리가 부족하지 않다면 굳이 제거하지 않습니다. 대상.. 2022. 6. 5.
객체 생성과 파괴 불팔요한 객체 생성을 피하라 똑같은 기능의 객체를 매번 생성하기 보다는 하나를 사용해서 재 사용 하는 것이 성능면에서나 메모리 관리면에서 더 좋다. String s = new String("bikini"); // 잘못된 사용 String s = "bikini"; // 옳은 사용 위에 코드처럼 작성하게 되면 반복문일 경우 객체가 계속해서 생성된다. 책에서도 Boolean(String)을 쓰지말고, Boolean.valueOf(String)을 사용하라고 나와 있다. @HotSpotIntrinsicCandidate public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); } java코드를 보면 static으로 하나의 메서드만 생성해서 사용하도.. 2022. 6. 1.
객체 생성과 파괴 인스턴스화를 막으려거든 private 생성자를 사용하라. 정적 메서드와 정적 필드만을 담은 클래스를 만들고 싶을 때 private 생성자를 사용해서 사용하지 않도록 명시하자. public class UtilityClass { private UtilityClass() { throw new AssertionError() } } 혹시나 내부에서 사용 할 수도 있기 때문에 AssertionError()를 던져주자. 장점 1. 상속을 막아준다. 2. 사용하지 말라는 표현이 명백하다. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라. 아래 코드들은 final로 인해 유연하지 못하다. 반드시 하나의 객체만을 사용한다는 가정이 없이는 사용하기 어렵고 final제거 한 후 교체하는 방식도 어색하며 오류를 내구 쉽다.. 2022. 6. 1.
객체 생성과 파괴 생성자에 매개변수가 많다면 빌더를 고려하라. 책에서는 빌더 패턴 전 시기에 2가지를 설명하고 있지만 결론은 builder를 사용하라는 것이기 때문에 builder에 대해서만 정리하였다. 빌더 패턴을 쓰면 장점 1. 빌더 패턴은 계층적으로 설계된 클래스와 함께 쓰기에 좋다. return 타입을 지정해서 child class에서 타입을 강제 반환 시킬 수 있다. 2. 가독성이 좋으며 build()에서 유효성 검사를 할 수 있다. private 생성자나 열거 타입으로 싱글턴임을 보증하라. public static 방식 public class Elvis() { public static final Elvis INSTANCE = new Elvis(); // 생성하지 못하도록 막는다. private Elvis() {.. 2022. 5. 22.
객체 생성과 파괴 생성자 대신 정적 팩터리 메서드를 고려하라. 정적 팩토리의 장점 이름을 가질 수 있다. 생성자의 이름으로는 반환될 객체의 특성을 제대로 설명하지 못한다. 반면 정적 팩토리 메서드는 특징을 묘사하게 할 수 있다. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다. 불변 클래스는 인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있다. 또한, 인스턴스 통제 클래스를 통해 어느 인스턴스를 살아있게 할지 철저히 통제 할 수 있다. 인스턴스 통제 클래스는 Flyweight Pattern을 기반으로 되어있다. 어떤 인스턴스는 싱글톤으로 만들 수 있고, 다른 것은 인스턴스화 불가로도 만들 수 있다. 반환 타입이 하위 타입 객체를 반환 할 수 있는 능력이 .. 2022. 5. 9.
현대적 C++에 적응하기 현대적 C++에 적응하기 항목 7 : 객체 생성 시 괄호와 중괄호를 구분하라 변수를 선언한 방법은 크게 3가지이다. int x(0); int y = 0; int z{0}; 균일 초기화(uniform initialization) C++11은 위에서 처럼 중괄호({..})로 초기화 할 수 있는 방법이 있다. 멤버의 초기 값을 지정하는 데에도 사용 할 수 있다. class Widget { private : int x{0}; int y = 0; int z(0); // 오류 } 복사 할 수 없는 객체를 초기화 할 수 있다. std::atomic ai1{ 0 }; std::atomic ai2( 0 ); std::atomic ai3 = 0; // 오류 암묵적 좁히기(narrowing conversion) 변환을 방지.. 2021. 10. 17.
Effective modern C++ ( Auto ) Auto 항목 6 : auto가 원치 않은 형식으로 연역될 때에는 명시적 형식의 초기치를 사용하라. 컨테이너를 참조하는 operation[]은 보통 &T를 돌려주지만 bool의 경우는 다르다. 왜냐하면 C++에서는 bool처럼 bit에 대한 형식에 대해서 참조하는 것이 금지되어 있다. std::vector features(const Widget& w); Widget w; auto highPriority = features(w)[5]; // 임시객체 vector 파괴된다. processWidget(w, highPriority); 위 코드를 보았을 때 bool일 경우 이야기 해보자! 1. features는 처음에 임시객체 vector을 return한다. 2. 임시객체 vector의 5번 객체를 찾아서 std.. 2021. 10. 16.
Effective modern C++ ( Auto ) Auto 항목 5 : 명시적 형식 선언보다는 auto를 선호하라 Auto를 사용하지 않을 때 불편한 점 1. 초기화 하지 않을 때, 쓰레기 값이 들어갈지 초기 값(0 or "")이 들어갈지 모른다. 2. 긴 형식을 짧게 만들 수 있다. ( ex typename std::iterator_traits::value_type ) 3. closure형식으로 지역변수를 선언 할 때, 타입을 명시하는 것이 어렵다. auto를 사용 할 때 무조건 초기 값을 넣어야 한다. 컴파일러가 auto를 연역하려면 초기값이 필요하므로 초기 값을 넣지 않은 실수를 방지 할 수 있다. int x1; // 문맥에 따라서는 초기화되지 않을 수 있음 auto x2; // 오류! 초기치가 꼭 필요함 auto x3 = 0; // 양호함; x3.. 2021. 10. 16.
Effective modern C++ ( 형식 언어 ) 형식언어 항목 4: 연역된 형식을 파악하는 방법을 알아두라 IDE편집기 마우스 커서를 올려 놓으면 연역을 확인해 준다. 컴파일러 진단 메시지 원하는 형식에 컴파일러 에러 문제가 발생하게 만드는 것이다. template class TD; auto x = theAnswer; auto y = &theAnswer; TD xType; TD yType; error: aggreate 'TD xType' has incomplete type and cannot be defined. error: aggreate 'TD yType' has incomplete type and cannot be defined. 실행시점 출력 2021. 10. 14.
일반화된 자료구조와 함수 다형성 일반적인 타입으로 매개변수 화 할 수 있어 여러 구체화된 정의를 갖을 수 있다. struct Pair { first: T, second: T }위의 코드 예제를 보면 T부분에 int, str 어떤 것이 와도 상관이 없다. 이를 제네릭 함수라고 부른다. let magic_pair: Pair = Pair { first: 7, second: 42 }; let pair_of_magicians: Pair = Pair { first: "Gandalf", second: "Sauron" };일반화 된 만큼 함수의 일반화도 중요하다. fn second(pair: Pair) { pair.second; // 2번째 인수를 꺼내는 함수 } let a = second(magic_pair); // 호출오류처리 스레드가 모.. 2021. 10. 10.
고차원 함수와 매개변수화를 통한 코드 일반화 고차원 함수와 클로저 고차원 함수 : 다른 함수를 매개변수로 받는 함수; 사이에 매개변수로 받을 함수의 타입을 적어야 한다. fn again i32 >(f: F, s: i32) -> i32 { f(f(s)) } // 고차원 함수 fn triples(s: i32) -> i32 { 3 * s } fn main() { let mut strength = 26; println!("My tripled strength equals {}", triples(strength)); // 26 println!("My strength is still {}", strength); // 26 strength = triples(strength); println!("My strength is now {}", strength);.. 2021. 10. 9.
패턴매칭 콘솔에서 입력 받기 입출력 기능 : std::io모듈을 통해 처리된다. 세상에서 가장 안전한 언어 답게 ok() // 정당하게 except() // 함수가 정확하게 작동하지 않았을 때 추가하여 사용해줘야만 한다. use std::io; fn main () { println!("What's your name. noble warrior?"); let mut buf = String::new(); io::stdin().read_line(&mut buf) .ok() .expect("Failed to read ljine"); println!("{}, that's a mighty name indeed!", buf.trim()); let input_num: Result = buf.trim().parse(); print.. 2021. 10. 8.
데이터 구조화 문자열 러스트는 두가지 유형의 문자열로 구성되어 있다. &str : 문자열 슬라이스 타입 ; 불변하며 고정된 크기를 갖는다. let magician1 = "Merlin"; let greeting = "Hello, !"; &'static str: 문자열이 정적으로 할당되었음을 의미 프로그램이 끝날 때까지 존재(정적 수명 - static lifetime - ) let magician2: &'static str = "Gandalf"; 동적 문자열 만드는 방법 &[..] : 리소스를 소모하지 않음 to_string() : 힙 메모리를 할당함 let mut str1 = String::new(); let mut str2 = String::with_capacity(25); let mut str3 = magician1.. 2021. 10. 6.
함수와 제어문 조건문 특별하게 다른건 없다 fn main () { let dead = false; let health = 48; if dead { println!("Game Over!"); return; } if dead { println!("Game Over!"); return; } else { println!("You still have a chance to win!"); } if health >= 50 { println!("Continue to fight!"); } else if health >= 20 { println!("Stop the battle and gain strength"); } else { println!("Hide and try ro recover!"); } }cpp와는 다르게 코드 블록으로 변수.. 2021. 10. 6.
변수 출력 형식 o: 8진수 x: 소문자 16진수 X: 대문자 16진수 p: 포인터 b: 2진수 e: 소문자 지수 표기 E: 대문자 지수 표기 ?: 디버깅 목적으로 사용 u: unsigned integer 타입선언 let n: i32;전역 변수 static MAX_SIZE: i32 = 32; static MAX_NAME: &'static str = "HELLO WORLD"; fn main() { println!("{}", MAX_NAME); }변경 가능한 변수 기본적으로 rust는 변수를 변경 할 수 없다. mut을 선언해주어야 가능하다. let mut fuel = 34; fuel = 60;변수의 scope 기존 C++, JAVA등과 크게 차이가 없어 보인다. 외부 블록의 변수를 내부 블록에서 사용 할.. 2021. 10. 4.
Rust의 장점 및 특징 Rust의 장점 1. 강력한 타입 시스템을 통한 높은 안전성 제공 2. 저수준의 리소스들을 제어할 수 있기 때문에 하드웨어에 가깝게 실행 가능 Rust의 특징 : 안전성, 속도, 동시성 안전성 Rust에서 변수들의 입력은 컴파일이 되어 있기 때문에 정적이며 강력하다. 러스트의 컴파일러는 대부분 타입을 추론할 수 있기 때문에 모든 항목에 대해 타입을 표시하라고 강요하지 않는다. Rust의 컴파일러는 컴파일 동안 메모리 충돌, 누수 등,, 여러가지 문제를 감지 할 수 있다. 네이티브 코드를 컴파일한다. 동시성 동시성을 위한 매커니즘 중 하나로 얼랭에서 잘 알려진 액터 모델을 채택 스레드라고 부르는 경량 프로세스들이 병렬로 수행한다. 힙 메모리를 공유하지 않고 채널을 통해 데이터를 전달 2021. 10. 4.
Value INTRO C++에서 auto, decltype등을 자세히 이해하기 위해서는 value에 대해서 정확히 이해하고 넘어갈 필요가 있다. 다른 언어들은 메모리 관리를 대부분 가비지컬렉터에서 해주지만, 시스템 외에 개발자가 직접 관리 할 수 있다. 장점인지 단점인지는 잘 모르겠다. 하지만, 이 메모리 여부에 따라 Value의 범주가 구분된다. Copy & Move Value에 대해서 설명하기 전에 Copy와 Move에 대해서 알고 지나가야된다. 아래 그림을 참고 하자. Value의 유형 glvalue : 계산에서 개체, 비트 필드 또는 함수의 ID를 결정하는 식입니다. prvalue : 계산에서 개체 또는 비트 필드를 초기화하거나 연산자가 나타나는 컨텍스트에서 지정한 대로 연산자의 연산자 값을 계산하는 식입니.. 2021. 10. 4.
Effective modern C++ ( 형식 언어 ) 항목 3 : decltype의 작동 방식을 숙지하라 auto, decltype은 operator[]에 따라 다른 값을 돌려 줄 수 있다. ( 왜 그런지는 항목 6에서 확인 ) Widget w; auto myWidget1& cw = w; auto myWidget1 = cw; // auto의 형식 연역 : // myWidget1의 형식 Widget decltype(auto) myWidget2 = cw; // decltype 형식 연역 : // const Widget& 그렇다면 이 2개를 어떻게 맞출 수 있을까? C++11 : 후행 반환 형식(trailing return type)을 통해 auto의 타입형식을 decltype과 일치 시킬 수 있다. // 아래 코드는 정련 할 필요가 있다. template au.. 2021. 9. 26.
C++의 람다 식 람다식을 쓰는 이유 : 람다식은 함수의 인라인화 가능하다. 메모리 관리에 용이하다. 람다식은 실행 하고 난 뒤 메모리에서 바로 삭제된다. C++람다식의 형태 []() ->void { cout 2021. 9. 22.
Effective modern C++ ( 형식 언어 ) 형식언어 항목 2 : auto의 형식 연역 규칙을 숙지하라 항목 1과 거의 동일하다. ( auto를 ParamType으로 대체하여 생각하면 된다. ) 형식 지정자가 포인터나 참조형식이지만 보편 참조는 아닌 경우 형식 지정자가 보편 참조인 경우 형식 지정자가 포인터도 아니고 참조도 아닌 경우 auto x = 27; // 경우 3 ( x는 포인터도 아니고 참조도 아님 ) const auto cx = x; // 경우 3 ( cx 역시 둘 다 아님 ) const auto& rx = x; // 경우 1 ( rx는 보편 참조가 아닌 참조 ) auto&& uref1 = x; // x는 int이자 l-value이므로 // uref1의 형식은 int& auto&& uref2 = cx; // cx는 const int이자 .. 2021. 9. 22.
Effective modern C++ ( 형식 언어 ) 형식 연역 항목 1: 템플릿 형식 연역 규칙을 숙지하라 필요성 : 템플릿 형식 연역 규칙들이 auto의 문액에 적용 될 때 덜 직관적임 auto를 잘 활용하려면 템플릿 형식 연역을 이해하고 있어야 한다. T, ParamType서로 다르게 연역된다. template void f(ParamType param) f(expr) *위의 식은 아래에서 parameter를 말하기 위해서 사용한다. 보편 참조(universal reference)가 아닌 경우 ( 포인터 or 참조 형식 ) 만일 expr이 참조 형식이면 참조 부분을 무시 expr의 형식을 ParamType에 대해 패턴 부합(pattern-matching)방식으로 대응시켜 T의형식을 결정 template void f(T& param); int x = 27.. 2021. 9. 21.
C++최적화 (2단원) 2. 컴퓨터 하드웨어의 최적화 - 왜 최적화가 필요한지 알 수 있다. - 프로세서의 구조를 통해서 어떤 방법으로 최적화해야 하는지 유추하는 방법을 알아봄. C++은 컴퓨터의 거짓말을 믿습니다. C++ 프로그램은 '마치' 명령문을 순서대로 실행하는 것처럼 작동하기만 하면 됩니다. C++ 컴파일러와 컴퓨터 자체는 계산의 의미가 변경되지 않은 선에서 실행순서를 변경하여 프로그램 실행을 빠르게 할 수도 있습니다. C++11부터는 단일 실행 주소만 있다고 믿지 않습니다. 이제 C++표준 라이브러리는 스레들 시작 및 중지하고 스레드 간에 메모리 접근을 동기화하는 기능을 제공한다. 특정 메모리 주소는 일반 메모리가 아닌 장치 레지스터가 될 수 있다. 동일한 스레드가 두 번 연속해서 읽는 사이에 변할 수 있으며,이는 .. 2021. 9. 21.