✏️ 2022-11-21 Today I Learn

@mitoconcrete · November 21, 2022 · 13 min read

[알고리즘] 약수구하기 & 약수의 갯수 구하기

  • n의 약수를 구할 때는, a * b == n 이 되는 규칙을 항상 따른다.
  • 따라서, n 번이 아니라 1/2n 번 만 반복문을 돌아도 된다. 따라서, 제곱근을 이용해 절반횟수만 반복문을 돌 수 있도록 설정한다.
  • i 와 i // j 를 하면 한 사이클에서 2개의 약수를 구할 수 있다.
  • 단 약수가 같은 수인 경우를 제외시키기 위해서 조건문을 이용하여 필터링 한다.
def solution(number, limit, power):
    answer = 0
    for i in range(1, number + 1):
        divider_cnt = 0
        print(f'{i} 일 때 {int(i**(1/2))} 번 반복문을 거친다.')
        for j in range(1, int(i**(1/2)) + 1):
            if i % j == 0:
                print(f'{i}의 약수 {j}')
                divider_cnt += 1
                if ((j**2) != i):
                    divider_cnt += 1
                    print(f'{i}의 약수 {i // j}')
        if divider_cnt > limit:
            divider_cnt = power
        answer += divider_cnt
    return answer

[객체지향] 객체지향 Study

1. setter 을 사용하지 말라?

setter를 쓰지말라고 하고 그냥가버리면 어떡해요..

setter를 사용하지마란 의미는, 절대 사용하지 말라는 말라기보단 객체의 책임 역할 핵심에 집중하여 의미없는 set을 이용해 멤버변수의 무분별한 업데이트를 막자는 의미이다. setPrice 를 해놓고 discount 한 price를 전달하여 업데이트한다면, 그것은 의미와 맞지 않는다. 만약 불가피하게 사용해야만 한다면, 구체적인 네이밍을 명시해줌으로서 모두가 납득할만한 setter를 배치해야한다. 하지만, 사용하지 않는 것을 추천한다.

2. SOLID 5원칙

  • SRP(Single Responsibility Principle) : 단일 책임 원칙

    • 한 클래스는 하나의 책임만을 가져야한다. 어떤 클래스를 수정했을 때, 다른 부분에서의 변경이 거의 없다면, 이 원칙을 잘 지킨 것 이다. 클래스를 수정하는 기준은 유일해야한다. 이유는 클래스는 단 하나의 책임을 가지고 있기 때문이다.
  • OCP(Open/Closed Principle) : 개방/폐쇄의 원칙

    • 확장에는 개방적이나, 변경에는 닫혀있어야 한다. 하나의 참조변수로 여러타입의 변수를 선언 할 수 있는 것을 지향(Open)해야하지만, 단일 메소드를 변경하는 것을 지양(Close)한다.
    • 어떤 클래스에 걷기기능만 있다가 뛰기, 수영하기 등의 기능이 나중에 추가되었다고 했을 때, 계속 걷기 메소드를 수정하여 기능을 추가하기 보다는, 각 기능에 대한 인터페이스를 생성하고 그것을 상속받아 추가하는 것이 더 적은양의 코드를 수정하면서 기능을 구현 할 수 있다.
  • LSP(Liskov Subsitution Principle) : 리스코프 치환 원칙

    • 서브타입은 언제나 상위타입으로 치환 할 수 있어야한다. 상속으로 이를 구현 할 수 있으며, 주의할 점은 상호합의된 정합성을 깨뜨리면 안된다.
    • 이 원칙을 지키지 않으면, 메소드가 이상하게 동작할 수 있으며, 이는 테스트를 통해 검증이 되지않기에 주의해야한다.
  • ISP(Interface Segregation Principle) : 인터페이스 분리 원칙

    • 클라이언트가 자신이 사용하지 않는 메소드에 의존하면 안된다.
    • 범용 인터페이스를 제공하기 보단, 역할에 맞는 인터페이스를 각각 만들어 상속하여 실행하는게 목적에 맞다.
  • DIP(Dependency Inversion Principle) : 의존관계 역전 원칙

    • 객체간 협력과정에서 의존관계가 형성되는데, 변하기 어려운 것을 기준으로 삼아 의존관계를 만드는 것이 좋다.
    • 변하기 어려운 것은 추상적인 개념이나 흐름이고, 변하기 쉬운것은 구체적인 행동을 의미한다.

SOLID 원칙을 발전시켜 만든 것이 디자인패턴이다. SOLID 원칙을 지키다보면 자연스레 디자인 패턴을 쓰고 있을 수 있다!

3. 추상화 vs 구체화

  • 추상화는 어떤 사물에게서 공통적 성질을 뽑아 개념화하는 것이다.
  • 구체화는 어떤 사물의 특징을 세부적으로 구현하는 것이다.
  • 상속을 위한 부모를 정의하는 것은 추상화에 가깝고, 그것을 상속하면 상속할수록 구체화가 더해진다.

[Java] Java Study

1. 상속

  • 생성자와 초기화 블록은 상속되지 않는다.
  • 자식클래스의 멤버변수는 부모클래스의 멤버변수 갯수와 같거나 항상크다.

2. 오버라이딩

오버라이딩 시 접근제어자와 Exception은 제한된 조건 안에서 오버라이딩이 가능하다.

  • 접근제어자는 부모보다 권한이 작아질 수 없다.
  • 부모보다 더 넓은 범위의 Exception을 선언 하는 것은 불가하다.
  • static 메소드를 인스턴스 메소드로 변경하는 것은 불가하다.
  • 인스턴스 메소드를 static 메소드로 변경하는 것은 불가하다.

3. super

  • 클래스는 지역변수로 자신의 주소값을 사용 할 수 있다. 그것이 this인데 상속은 받은 클래스는 super를 지역변수로 사용이 가능하다. this, super 모두 인스턴스가 생성되어야 사용가능하기 때문에, static 메소드에서는 사용이 불가하다.

4. super()

  • 자식클래스가 인스턴스화 될 때, 부모클래스의 생성자를 우선적으로 실행시켜 부모클래스의 멤버들을 초기화 시켜주어야 한다. 자바 컴파일러는 잠재적으로 super()생성자를 실행시켜준다.

5. 패키지

  • 하나의 디렉토리이자, class 보따리

6. import

  • java.lang.*과 같이 *을 사용하여 모든 클래스를 가져와도, 실행속도에는 큰 영향이 없다.
  • static 클래스를 호출하면 클래스이름을 생략할 수 있다.

7. 제어자

  • 접근제어자는 단하나만 사용가능하며, 이외 제어자들은 조합이 가능하다.
  • final : 변경할 수 없는 값에 선언하며, 이 키워드를 이용하면, 상속 및 오버라이딩 확장이 불가하다.
  • 제어자 조합주의 :

    • static, abstract : static은 무조건 몸통이 존재해야함
    • abstract, final : 역할 상충(상속을 해야 이용 가능한 것 vs 상속이 불가한 것)
    • abstract, private : 상속받는 곳에서 private으로 선언한 것에 접근 할 수 없다.
    • private, final : 의미가 비슷하다.

8. 다형성

  • 참조변수의 타입에 따라, 사용할 수 있느 멤버변수의 갯수가 달라진다.
  • 부모의 참조변수로 자식을 사용할 수 없는 이유는 사용하는 멤버변수가 자식이 항상 같거나 많기 때문이다.
  • 메소드의 경우 참조변수의 타입에 관계없이, new 를 통해 선언한 인스턴스의 메소드를 사용하지만, 멤버변수는 참조변수를 따라간다.
  • 인스턴스 변수에 직접 접근하게 되면, 참조변수가 무엇이냐에 따라 값이 변할 수 있기 때문에, 직접적인 접근은 지양한다.

9. 형변환

  • 참조변수의 타입을 강제로 정의하여, 사용할 수 있는 멤버의 범위를 조정한다. 인스턴스자체가 바뀌는 것은 아님.

10. instanceof

  • 자신이 속한 모든 인스턴스에 true를 반환한다. 단 getName을 통해 접근하면, 참조변수가 가리키고 있는 인스턴스이름을 반환한다.

11. 인터페이스

  • 인터페이스의 모든 멤버변수는 public static final로 정의한다.
  • 인터페이스의 장점은 클래스 간의 관계를 느슨하게 만들어준다는 것이다. 클래스 A와 B가 있다고 했을 때, A는 B클래스를 매개변수로 받아 사용하는 메소드가 있다. 따라서, A클래스를 사용하기 위해선, 반드시 B클래스의 정의가 필요하다. 하지만, 인터페이스를 사용하게 되면 의존관계가 되는 기능만 선언하고 해당 인터페이스만 매개변수로 전달하여 사용하면 되니, B클래스를 굳이 선언 할 필요가 없어지기에, 의존관계가 클래스-클래스를 사용하는 것 보단 훨씬 느슨해진다.
// Before
class A {
    public void BUsage(B b){
        b.write()
    }
}

class B {
    public void write(){
        ...
    }
}

// After
class A{
    public void BUsage(I i){
        i.write()
    }
}

interface I{
    public void write();
}

class B implements I{
    ...
}
  • 인터페이스에서 새로운 메서드를 추가하는 일은 해당 인터페이스를 사용하는 모든 클래스에 영향을 줄 수 있다. 따라서 defalt메소드를 인터페이스에 추가 할 수 있는 기능이 추가되었다.

12. 내부 클래스

  • 두개의 클래스가 긴밀한 관계에 있을 때, 따로 선언해주는 것보다 내부에 선언해주어 코드의 복잡성을 줄여준다.
  • 외부 클래스의 멤버변수처럼 사용되기도 하고, 메서드내에서 지역변수처럼 사용되기도 한다.

13. 익명클래스

  • 클래스 선언과 인스턴스 생성을 동시에 하는 클래스. 일회용 클래스이다.

[회고] 221121

자바의 객체지향 세계에 녹아들기에 배워야 할 것들, 신경써야할 것들이 너무나도 많다. 몰입해보자!!

@mitoconcrete
어제보다 조금 더 성장하기 위해 기록합니다.