JavaRush /Java Blog /Random-KO /코드 작성 규칙: 시스템 생성부터 객체 작업까지

코드 작성 규칙: 시스템 생성부터 객체 작업까지

Random-KO 그룹에 게시되었습니다
안녕하세요 여러분. 오늘은 올바른 코드 작성에 대해 이야기하고 싶습니다. 제가 처음 프로그래밍을 시작했을 때 이렇게 써도 된다는 내용이 어디에도 명확하게 적혀 있지 않았는데, 이렇게 쓰면 내가 찾아갈 테고… 그 결과, 올바르게 작성하는 방법, 프로그램의 특정 섹션에서 어떤 원칙을 따라야 하는지 등 머릿속에 많은 질문이 생겼습니다. 코드 작성 규칙: 시스템 생성부터 객체 작업까지 - 1글쎄, 모든 사람이 Clean Code와 같은 책에 즉시 뛰어들고 싶어하는 것은 아닙니다. 책에 많은 내용이 기록되어 있기 때문이지만 처음에는 명확하지 않습니다. 그리고 책을 다 읽을 때쯤에는 코딩에 대한 모든 욕구를 좌절시킬 수 있습니다. 그래서 오늘은 위의 내용을 바탕으로 더 높은 수준의 코드를 작성하기 위한 작은 가이드(작은 권장 사항 모음)를 제공하고 싶습니다. 이 글에서는 시스템 생성과 인터페이스, 클래스, 객체 작업과 관련된 기본 규칙과 개념을 살펴보겠습니다. 이 자료를 읽는 데 시간이 많이 걸리지 않으며 지루해지지 않기를 바랍니다. 위에서 아래로, 즉 애플리케이션의 일반적인 구조부터 보다 구체적인 세부 사항까지 살펴보겠습니다. 코드 작성 규칙: 시스템 생성부터 객체 작업까지 - 2

체계

시스템의 일반적으로 바람직한 특성은 다음과 같습니다.
  • 최소한의 복잡성 - 지나치게 복잡한 프로젝트는 피해야 합니다. 가장 중요한 것은 단순성과 명확성입니다(최고 = 단순함).
  • 유지 관리 용이성 - 애플리케이션을 생성할 때 지원이 필요하다는 점을 기억해야 합니다(귀하가 아니더라도). 따라서 코드는 명확하고 명확해야 합니다.
  • 약한 결합은 프로그램의 서로 다른 부분 사이의 최소 연결 수입니다(OOP 원칙의 최대 사용).
  • 재사용성 - 다른 애플리케이션에서 조각을 재사용할 수 있는 기능을 갖춘 시스템 설계
  • 이식성 - 시스템은 다른 환경에 쉽게 적응되어야 합니다.
  • 단일 스타일 - 다양한 조각의 단일 스타일로 시스템을 설계합니다.
  • 확장성(확장성) - 기본 구조를 방해하지 않고 시스템을 개선합니다(조각을 추가하거나 변경하는 경우 나머지에는 영향을 미치지 않습니다).
기능을 추가하지 않고 수정이 필요 없는 애플리케이션을 구축하는 것은 사실상 불가능합니다. 우리의 아이디어가 시대에 뒤처지지 않도록 끊임없이 새로운 요소를 도입해야 합니다. 그리고 이것이 바로 확장성이 중요한 역할을 하는 부분입니다 . 확장성은 본질적으로 애플리케이션 확장, 새로운 기능 추가, 더 많은 리소스(즉, 더 많은 로드)로 작업하는 것입니다. 즉, 새로운 로직을 추가하기 쉽도록 모듈성을 높여 시스템의 결합을 줄이는 등 몇 가지 규칙을 준수해야 합니다.

시스템 설계 단계

  1. 소프트웨어 시스템 - 일반적인 형태로 애플리케이션을 설계합니다.
  2. 하위 시스템/패키지로 분리 - 논리적으로 분리 가능한 부분을 정의하고 해당 부분 간의 상호 작용 규칙을 정의합니다.
  3. 하위 시스템을 클래스로 나누기 - 시스템의 일부를 특정 클래스와 인터페이스로 나누고 이들 간의 상호 작용을 정의합니다.
  4. 클래스를 메서드로 나누는 것은 해당 클래스의 작업을 기반으로 클래스에 필요한 메서드를 완전히 정의하는 것입니다. 분석법 설계 - 개별 분석법의 기능을 자세히 정의합니다.
일반적으로 일반 개발자가 설계를 담당하고, 애플리케이션 아키텍트가 위에서 설명한 항목을 담당합니다.

시스템 설계의 주요 원리 및 개념

지연 초기화 관용구 애플리케이션은 객체가 사용될 때까지 객체를 생성하는 데 시간을 소비하지 않으므로 초기화 프로세스 속도가 빨라지고 가비지 수집기 로드가 줄어듭니다. 하지만 모듈화 위반으로 이어질 수 있으므로 너무 멀리 진행해서는 안 됩니다. 모든 디자인 단계를 특정 부분(예: 메인)이나 공장 처럼 작동하는 클래스로 옮기는 것이 좋습니다 . 좋은 코드의 측면 중 하나는 자주 반복되는 상용구 코드가 없다는 것입니다. 일반적으로 이러한 코드는 적절한 시점에 호출될 수 있도록 별도의 클래스에 배치됩니다. AOP 이와 별도로 관점 지향 프로그래밍에 대해 언급하고 싶습니다 . 이는 엔드투엔드 로직을 도입하여 프로그래밍하는 것입니다. 즉, 반복되는 코드를 클래스(Aspect)에 넣고 특정 조건에 도달하면 호출되는 것입니다. 예를 들어 특정 이름의 메서드에 액세스하거나 특정 유형의 변수에 액세스하는 경우입니다. 코드가 어디에서 호출되는지 즉시 명확하지 않기 때문에 측면이 혼란스러울 수 있지만 그럼에도 불구하고 이는 매우 유용한 기능입니다. 특히 캐싱이나 로깅 시 일반 클래스에 추가 로직을 추가하지 않고 이 기능을 추가합니다. OAP에 대한 자세한 내용은 여기에서 확인할 수 있습니다 . Kent Beck에 따른 단순한 아키텍처 설계를 위한 4가지 규칙
  1. 표현성 - 클래스의 명확하게 표현된 목적에 대한 필요성은 올바른 이름 지정, 작은 크기 및 단일 책임 원칙 준수를 통해 달성됩니다(아래에서 자세히 살펴보겠습니다).
  2. 최소한의 클래스와 메소드 - 클래스를 가능한 한 작고 단방향으로 나누고자 한다면 너무 멀리 갈 수도 있습니다(안티패턴 - 샷건닝). 이 원칙은 시스템을 컴팩트하게 유지하고 너무 멀리 나아가지 않고 재채기할 때마다 클래스를 생성하는 것을 요구합니다.
  3. 중복 부족 - 혼동을 주는 추가 코드는 시스템 설계가 좋지 않다는 신호이며 별도의 장소로 이동됩니다.
  4. 모든 테스트 실행 - 모든 테스트를 통과한 시스템이 제어됩니다. 왜냐하면 어떤 변경이라도 테스트 실패로 이어질 수 있기 때문입니다. 이는 메서드의 내부 논리 변경으로 인해 예상되는 동작도 변경되었음을 보여줄 수 있습니다. .
SOLID 시스템을 설계할 때 SOLID의 잘 알려진 원칙을 고려하는 것이 좋습니다. S - 단일 책임 - 단일 책임 원칙; O - 개방형-폐쇄형 - 개방성/밀폐성의 원칙; L - Liskov 대체 - Barbara Liskov의 대체 원리; I - 인터페이스 분리 - 인터페이스 분리의 원리. D - 종속성 반전 - 종속성 반전 원칙; 각 원칙에 대해 구체적으로 설명하지는 않겠습니다. (이 내용은 이 기사의 범위를 조금 벗어나지만 여기에서 자세한 내용을 확인할 수 있습니다.

상호 작용

아마도 적절한 클래스를 만드는 가장 중요한 단계 중 하나는 클래스의 구현 세부 사항을 숨기는 좋은 추상화를 나타내는 동시에 서로 명확하게 일치하는 메서드 그룹을 나타내는 적절한 인터페이스를 만드는 것입니다. . SOLID 원칙 중 하나인 인터페이스 분리 에 대해 자세히 살펴보겠습니다. 클라이언트(클래스)는 사용하지 않을 불필요한 메서드를 구현해서는 안 됩니다. 즉, 이 인터페이스의 유일한 작업을 수행하는 것을 목표로 하는 최소한의 메서드로 인터페이스를 구축하는 것에 대해 이야기하고 있다면(저에게는 단일 책임 과 매우 유사합니다 ), 두 개의 더 작은 인터페이스를 만드는 것이 좋습니다 하나의 부풀어 오른 인터페이스 대신 하나. 다행스럽게도 상속의 경우처럼 클래스는 둘 이상의 인터페이스를 구현할 수 있습니다. 또한 인터페이스의 올바른 이름 지정에 대해서도 기억해야 합니다. 이름은 해당 작업을 최대한 정확하게 반영해야 합니다. 그리고 물론 길이가 짧을수록 혼란이 줄어들 것입니다. 일반적으로 문서에 대한 주석이 작성되는 곳은 인터페이스 수준이며 , 이는 메서드가 수행해야 하는 작업, 인수 및 반환할 항목을 자세히 설명하는 데 도움이 됩니다.

수업

코드 작성 규칙: 시스템 생성부터 객체 작업까지 - 3수업의 내부 구성을 살펴 보겠습니다. 또는 오히려 클래스를 구성할 때 따라야 하는 몇 가지 관점과 규칙입니다. 일반적으로 클래스는 특정 순서로 정렬된 변수 목록으로 시작해야 합니다.
  1. 공개 정적 상수;
  2. 개인 정적 상수;
  3. 개인 인스턴스 변수.
다음은 인수가 적은 것부터 많은 것 순으로 다양한 생성자를 나열합니다. 그 다음에는 더 개방적인 액세스부터 가장 닫힌 액세스까지의 메서드가 이어집니다. 일반적으로 제한하려는 일부 기능의 구현을 숨기는 비공개 메서드는 맨 아래에 있습니다.

수업 규모

이제 학급 규모에 대해 이야기하고 싶습니다. SOLID의 원칙 중 하나인 단일 책임을코드 작성 규칙: 시스템 생성부터 객체 작업까지 - 4 기억해 봅시다 . 단일 책임 - 단일 책임의 원칙. 각 개체에는 단 하나의 목표(책임)가 있으며 모든 메서드의 논리는 이를 보장하는 것을 목표로 합니다. 즉, 이를 바탕으로 크고 비대해진 클래스(본질상 반패턴인 "신성한 객체")를 피해야 하며, 클래스에 다양하고 이질적인 논리의 메서드가 많이 있는 경우 다음과 같이 생각해야 합니다. 몇 개의 논리적 부분(클래스)으로 나누는 것에 대해 설명합니다. 결과적으로, 주어진 클래스의 대략적인 목적을 알면 메소드의 목적을 이해하는 데 많은 시간이 필요하지 않기 때문에 코드의 가독성이 향상됩니다. 또한 클래스 이름을 주의 깊게 살펴봐야 합니다 . 클래스 이름은 포함된 논리를 반영해야 합니다. 이름이 20개 이상의 단어로 구성된 클래스가 있는 경우 리팩토링을 고려해야 합니다. 모든 자존심이 강한 클래스에는 그렇게 많은 수의 내부 변수가 있어서는 안 됩니다. 실제로 각 메서드는 그 중 하나 또는 여러 개와 함께 작동하며, 이로 인해 클래스 내에서 더 큰 결합이 발생합니다(클래스가 단일 전체여야 하기 때문에 정확히 그래야 합니다). 결과적으로 클래스의 일관성을 높이면 클래스의 일관성이 감소하고 클래스 수가 늘어납니다. 어떤 사람들에게는 이것이 성가신 일인데, 특정 대규모 작업이 어떻게 진행되는지 확인하기 위해 수업에 더 많이 참석해야 하기 때문입니다. 무엇보다도 각 클래스는 다른 클래스와 최소한으로 연결되어야 하는 작은 모듈입니다. 이러한 격리로 인해 클래스에 로직을 추가할 때 변경해야 하는 변경 횟수가 줄어듭니다.

사물

코드 작성 규칙: 시스템 생성부터 객체 작업까지 - 5

캡슐화

여기에서는 우선 OOP의 원칙 중 하나인 캡슐화 에 대해 이야기하겠습니다 . 따라서 구현을 숨기는 것은 변수 사이에 메소드 계층을 생성하는 것으로 귀결되지 않습니다(단일 메소드, getter 및 setter를 통한 액세스를 무심코 제한하는 것은 캡슐화의 전체 지점이 손실되기 때문에 좋지 않습니다). 액세스를 숨기는 것은 추상화를 형성하는 것을 목표로 합니다. 즉, 클래스는 데이터 작업에 사용할 수 있는 일반적인 구체적인 메서드를 제공합니다. 하지만 사용자는 우리가 이 데이터를 어떻게 작업하는지 정확히 알 필요가 없습니다. 작동하면 괜찮습니다.

데메테르의 법칙

또한 디미터의 법칙(Law of Demeter)을 고려할 수도 있습니다. 이는 클래스 및 메서드 수준에서 복잡성을 관리하는 데 도움이 되는 작은 규칙 집합입니다. 따라서 개체가 있고 Car해당 개체에 메서드가 있다고 가정해 보겠습니다 move(Object arg1, Object arg2). 디미터 법칙에 따르면 이 메서드는 다음을 호출하는 것으로 제한됩니다.
  • 객체 자체의 메소드 Car(즉, this)
  • 에서 생성된 객체의 메소드 move;
  • 인수로 전달된 객체의 메서드 - arg1, arg2;
  • 내부 개체의 메서드 Car(동일함).
즉, 데메테르의 법칙은 어린이의 규칙과 같습니다. 친구와는 대화할 수 있지만 낯선 사람과는 대화할 수 없습니다 .

데이터 구조

데이터 구조는 관련 요소의 모음입니다. 객체를 데이터 구조로 간주할 때 객체는 메서드에 의해 처리되는 데이터 요소 집합이며 그 존재가 암시적으로 암시됩니다. 즉 저장된 데이터를 저장하고 연산(처리)하는 것을 목적으로 하는 객체이다. 일반 객체와의 주요 차이점은 객체가 존재가 암시되는 데이터 요소에 대해 작동하는 메서드 집합이라는 것입니다. 이해했나요? 일반 객체에서 주요 측면은 메소드이고 내부 변수는 올바른 작동을 목표로 하지만 데이터 구조에서는 그 반대입니다. 메소드는 여기서 주요 요소인 저장된 요소를 지원하고 작업하는 데 도움을 줍니다. 데이터 구조의 한 유형은 데이터 전송 개체(DTO) 입니다 . 이는 공용 변수가 있는 클래스이며 데이터베이스 작업 시 데이터를 전달하고 소켓에서 메시지를 구문 분석하는 등의 작업을 수행하는 메서드(또는 읽기/쓰기 메서드만)가 없습니다. 일반적으로 이러한 개체의 데이터는 오랫동안 저장되지 않으며 애플리케이션이 작동하는 엔터티로 거의 즉시 변환됩니다. 엔터티도 데이터 구조이지만 그 목적은 애플리케이션의 다양한 수준에서 비즈니스 로직에 참여하는 것이고 DTO는 애플리케이션과 데이터를 전송하는 것입니다. DTO 예:
@Setter
@Getter
@NoArgsConstructor
public class UserDto {
    private long id;
    private String firstName;
    private String lastName;
    private String email;
    private String password;
}
모든 것이 분명해 보이지만 여기서 우리는 잡종의 존재에 대해 배웁니다. 하이브리드는 중요한 로직을 처리하고 내부 요소와 액세스 메소드(get/set)를 저장하는 메소드를 포함하는 객체입니다. 이러한 개체는 지저분하고 새 메서드를 추가하기 어렵게 만듭니다. 요소를 저장하거나 일종의 논리를 수행하는 등 용도가 무엇인지 명확하지 않으므로 사용해서는 안됩니다. 여기에서 가능한 개체 유형에 대해 읽을 수 있습니다 .

변수 생성의 원리

코드 작성 규칙: 시스템 생성부터 객체 작업까지 - 6변수에 대해 조금 생각해 봅시다. 또는 오히려 변수를 생성하는 원칙이 무엇인지 생각해 보겠습니다.
  1. 이상적으로는 변수를 만들고 잊어버리는 것이 아니라 사용하기 직전에 변수를 선언하고 초기화해야 합니다.
  2. 가능할 때마다 변수를 final로 선언하여 초기화 후 값이 변경되는 것을 방지하세요.
  3. 카운터 변수를 잊지 마십시오. 일반적으로 우리는 일종의 루프에서 사용합니다 for. 즉, 재설정하는 것을 잊지 말아야 합니다. 그렇지 않으면 전체 논리가 중단될 수 있습니다.
  4. 생성자에서 변수를 초기화해야 합니다.
  5. 참조가 있는 객체 사용과 참조 없는 객체 사용 사이에서 선택 사항이 있는 경우 참조 없는new SomeObject() 객체 사용( )을 선택합니다 . 이 객체는 한 번 사용되면 다음 가비지 수집 중에 삭제되고 리소스를 낭비하지 않기 때문입니다.
  6. 변수의 수명을 최대한 짧게 만드세요(변수 생성과 마지막 액세스 사이의 거리).
  7. 루프를 포함하는 메서드의 시작 부분이 아닌 루프 바로 앞에서 루프에 사용되는 변수를 초기화합니다.
  8. 항상 가장 제한된 범위에서 시작하고 필요한 경우에만 확장하세요. 변수를 가능한 한 로컬로 만들어야 합니다.
  9. 각 변수는 한 가지 목적으로만 사용하십시오.
  10. 숨겨진 의미가 있는 변수는 피하세요. 변수는 두 작업 사이에서 분리되어 해당 유형이 그 중 하나를 해결하는 데 적합하지 않음을 의미합니다.
코드 작성 규칙: 시스템 생성부터 객체 작업까지 - 7

행동 양식

코드 작성 규칙: 시스템 생성부터 객체 작업까지 - 8논리 구현, 즉 메서드로 직접 이동해 보겠습니다.
  1. 첫 번째 규칙은 간결함입니다. 이상적으로는 하나의 메서드가 20줄을 넘지 않아야 하므로, 예를 들어 퍼블릭 메서드가 크게 "부풀어오르는" 경우 분리된 로직을 프라이빗 메서드로 옮기는 것을 고려해야 합니다.

  2. 두 번째 규칙은 if, 등의 명령 블록이 너무 많이 중첩되어서는 안 된다는 것입니다. else이렇게while 하면 코드 가독성이 크게 떨어집니다. 이상적으로 중첩은 두 블록을 넘지 않아야 합니다 {}.

    또한 이러한 블록의 코드를 간결하고 단순하게 만드는 것이 좋습니다.

  3. 세 번째 규칙은 메서드가 하나의 작업만 수행해야 한다는 것입니다. 즉, 메소드가 복잡하고 다양한 논리를 수행하는 경우 이를 하위 메소드로 나눕니다. 결과적으로 메서드 자체는 다른 모든 작업을 올바른 순서로 호출하는 것이 목적인 파사드가 됩니다.

    하지만 별도의 메소드를 생성하기에는 작업이 너무 단순해 보인다면 어떻게 해야 할까요? 예, 때로는 대포에서 참새를 쏘는 것처럼 보일 수도 있지만 작은 방법은 다음과 같은 여러 가지 이점을 제공합니다.

    • 더 쉬운 코드 읽기;
    • 메소드는 개발 과정에서 더욱 복잡해지는 경향이 있으며, 메소드가 처음에 단순했다면 기능을 복잡하게 만드는 것이 조금 더 쉬울 것입니다.
    • 구현 세부 정보를 숨깁니다.
    • 코드 재사용 촉진;
    • 더 높은 코드 신뢰성.
  4. 하향 규칙은 코드를 위에서 아래로 읽어야 한다는 것입니다. 즉, 코드가 낮을수록 논리의 깊이가 깊어지고, 반대로 높을수록 메소드가 더 추상화됩니다. 예를 들어, 스위치 명령은 매우 간결하고 바람직하지 않지만, 스위치를 사용하지 않고는 할 수 없다면 가능한 한 낮은 수준의 메서드로 이동해야 합니다.

  5. 메소드 인수 - 이상적인 개수는 몇 개입니까? 이상적으로는 전혀 없습니다.)) 그러나 실제로 그런 일이 발생합니까? 그러나 가능한 한 적은 수를 사용하도록 노력해야 합니다. 수가 적을수록 이 방법을 사용하기가 더 쉽고 테스트하기도 더 쉽기 때문입니다. 확실하지 않은 경우 입력 인수가 많은 메서드를 사용하는 경우의 모든 시나리오를 추측해 보세요.

  6. 별도로, 부울 플래그를 입력 인수로 갖는 메소드를 강조하고 싶습니다 . 이는 자연스럽게 이 메소드가 둘 이상의 연산(참이면 하나, 거짓 - 다른 연산)을 구현한다는 것을 의미하기 때문입니다. 위에서 쓴 것처럼 이는 좋지 않으며 가능하면 피해야 합니다.

  7. 메서드에 들어오는 인수가 많은 경우 (극단값은 7이지만 2~3개 이후에 생각해야 함) 일부 인수를 별도의 개체로 그룹화해야 합니다.

  8. 유사한 메소드가 여러 개 있는 경우 (오버로드됨) 유사한 매개변수를 동일한 순서로 전달해야 합니다. 이렇게 하면 가독성과 유용성이 향상됩니다.

  9. 매개변수를 메소드에 전달할 때 해당 매개변수가 모두 사용되는지 확인해야 합니다. 그렇지 않으면 인수는 무엇입니까? 인터페이스에서 잘라내면 그게 전부입니다.

  10. try/catch본질적으로 별로 좋아 보이지 않으므로 중간의 별도 메서드(예외 처리 메서드)로 옮기는 것이 좋습니다.

    public void exceptionHandling(SomeObject obj) {
        try {
            someMethod(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
위에서 코드 반복에 대해 이야기했지만 여기에 추가하겠습니다. 코드의 반복 부분이 포함된 메서드가 두 개 있는 경우 이를 별도의 메서드로 옮겨야 합니다. 그러면 메서드와 메서드 모두의 간결성이 높아집니다. 수업. 그리고 정확한 이름을 잊지 마세요. 이 기사의 다음 부분에서는 클래스, 인터페이스, 메소드 및 변수의 올바른 이름 지정에 대해 자세히 설명하겠습니다. 오늘은 그게 전부입니다. 코드 작성 규칙: 시스템 생성부터 객체 작업까지 - 9코드 규칙: 적절한 이름 지정, 좋은 댓글과 나쁜 댓글의 힘
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION