함수가 복잡하고 변수가 많은 경우 클래스를 만들어야합니까? 같은 언어로 불필요한 클래스를 작성하는

이 질문은 언어에 구애받지 않지만 완전히 다릅니다. 예를 들어, 객체 지향 프로그래밍 (OOP)은 파이썬 과는 달리 일류 함수가없는 Java 와 다릅니다 .

다시 말해서, Java와 같은 언어로 불필요한 클래스를 작성하는 데는 죄책감이 적지 만 Python과 같이 덜 언어적인 언어에는 더 나은 방법이 있다고 생각합니다.

내 프로그램은 여러 번 비교적 복잡한 작업을 수행해야합니다. 이 작업에는 많은 “부기”가 필요하며 일부 임시 파일 등을 작성하고 삭제해야합니다.

그렇기 때문에 많은 다른 “하위 작업”을 호출해야하는 이유도 있습니다. 하나의 거대한 방법으로 모든 것을 넣는 것은 그리 훌륭하지 않고 모듈 식이며 읽기 가능하지 않습니다.

이제 이것은 내 마음에 오는 접근법입니다.

1. 하나의 공용 메소드 만있는 클래스를 작성하고 서브 변수에 필요한 내부 상태를 인스턴스 변수로 유지하십시오.

다음과 같이 보일 것입니다.

class Thing:

    def __init__(self, var1, var2):
        self.var1 = var1
        self.var2 = var2
        self.var3 = []

    def the_public_method(self, param1, param2):
        self.var4 = param1
        self.var5 = param2
        self.var6 = param1 + param2 * self.var1
        self.__suboperation1()
        self.__suboperation2()
        self.__suboperation3()


    def __suboperation1(self):
        # Do something with self.var1, self.var2, self.var6
        # Do something with the result and self.var3
        # self.var7 = something
        # ...
        self.__suboperation4()
        self.__suboperation5()
        # ...

    def suboperation2(self):
        # Uses self.var1 and self.var3

#    ...
#    etc.

이 접근 방식에서 볼 수있는 문제는이 클래스의 상태가 내부적으로 만 의미가 있으며 유일한 공용 메소드 호출을 제외하고 인스턴스에서 아무것도 수행 할 수 없다는 것입니다.

# Make a thing object
thing = Thing(1,2)

# Call the only method you can call
thing.the_public_method(3,4)

# You don't need thing anymore

2. 클래스없이 여러 함수를 만들고 내부적으로 필요한 다양한 변수를 인수로 전달합니다.

내가 볼 수있는 문제는 함수간에 많은 변수를 전달해야한다는 것입니다. 또한 기능은 서로 밀접하게 관련되어 있지만 그룹화되지는 않습니다.

3. 2.처럼 상태 변수를 전달하는 대신 전역 변수로 만듭니다.

다른 입력으로 작업을 두 번 이상 수행해야하기 때문에 이것은 전혀 좋지 않습니다.

네 번째, 더 나은 접근법이 있습니까? 그렇지 않다면,이 방법들 중 어느 것이 더 좋을까요? 그 이유는 무엇입니까? 내가 놓친 것이 있습니까?



답변

  1. 하나의 공용 메소드 만있는 클래스를 작성하고 서브 변수에 필요한 내부 상태를 인스턴스 변수로 유지하십시오.

이 접근법에서 볼 수있는 문제는이 클래스의 상태가 내부적으로 만 의미가 있으며 유일한 공용 메소드 호출을 제외하고 인스턴스로 아무것도 할 수 없다는 것입니다.

옵션 1은 올바르게 사용 된 캡슐화 의 좋은 예입니다 . 당신이 원하는 내부 상태가 외부 코드에서 숨겨 질 수 있습니다.

그것이 당신의 클래스가 하나의 공개 메소드를 가지고 있다는 것을 의미한다면, 그렇게하십시오. 유지 관리가 훨씬 쉬울 것입니다.

OOP에서 정확히 하나의 일을하고 작은 공개 표면을 가지고 모든 내부 상태를 숨기고있는 클래스가 있다면 (Charlie Sheen이 말한 것처럼) WINNING 입니다.

  1. 클래스없이 함수를 많이 만들고 내부적으로 필요한 다양한 변수를 인수로 전달하십시오.

내가 볼 수있는 문제는 함수간에 많은 변수를 전달해야한다는 것입니다. 또한 기능은 서로 밀접하게 관련되어 있지만 그룹화되지는 않습니다.

옵션 2는 응집력이 낮습니다 . 유지 관리가 더 어려워집니다.

  1. 2와 같지만 상태 변수를 전달하는 대신 전역 변수로 만듭니다.

옵션 2와 같이 옵션 3은 응집력이 낮지 만 훨씬 더 심각합니다!

역사는 글로벌 변수의 편리함이 가져 오는 잔인한 유지 보수 비용보다 중요하다는 것을 보여주었습니다. 그렇기 때문에 캡슐화에 대해 항상 뛰어 다니는 것과 같은 오래된 방귀 소리가 들립니다.


우승 옵션은 # 1 입니다.


답변

# 1은 실제로 나쁜 옵션이라고 생각합니다.

당신의 기능을 고려하자 :

def the_public_method(self, param1, param2):
    self.var4 = param1
    self.var5 = param2 
    self.var6 = param1 + param2 * self.var1
    self.__suboperation1()
    self.__suboperation2()
    self.__suboperation3()

하위 작업 1은 어떤 데이터를 사용합니까? 하위 작업 2에서 사용하는 데이터를 수집합니까? 자체 저장하여 데이터를 전달할 때 기능이 어떻게 관련되어 있는지 알 수 없습니다. 자체를 살펴보면 일부 속성은 생성자, 일부는 the_public_method 호출 및 일부는 무작위로 다른 위치에 있습니다. 제 생각에는 엉망입니다.

2 번은 어떻습니까? 먼저 두 번째 문제를 살펴 보겠습니다.

또한 기능은 서로 밀접하게 관련되어 있지만 그룹화되지는 않습니다.

그것들은 하나의 모듈에있을 것이므로 완전히 그룹화 될 것입니다.

내가 볼 수있는 문제는 함수간에 많은 변수를 전달해야한다는 것입니다.

제 생각에는 이것이 좋습니다. 이것은 알고리즘의 데이터 의존성을 명시 적으로 만듭니다. 전역 변수 또는 자체 변수에 저장하면 종속성을 숨기고 덜 나빠 보이게 만들지 만 여전히 있습니다.

일반적으로 이러한 상황이 발생하면 문제를 분해 할 올바른 방법을 찾지 못했음을 의미합니다. 여러 가지 기능으로 잘못 나누는 것이 어색합니다.

물론 실제 기능을 보지 않으면 좋은 제안이 무엇인지 추측하기가 어렵습니다. 그러나 여기서 다루는 내용에 대해 약간의 힌트를 제공합니다.

내 프로그램은 여러 번 비교적 복잡한 작업을 수행해야합니다. 이 작업에는 많은 “부기”가 필요하며 일부 임시 파일 등을 작성하고 삭제해야합니다.

설치 프로그램이라는 설명에 맞는 예제를 선택하겠습니다. 설치 프로그램은 많은 파일을 복사해야하지만 도중에 취소하면 교체 한 파일을 모두 포함하여 전체 프로세스를 되 감아 야합니다. 이를위한 알고리즘은 다음과 같습니다.

def install_program():
    copied_files = []
    try:
        for filename in FILES_TO_COPY:
           temporary_file = create_temporary_file()
           copy(target_filename(filename), temporary_file)
           copied_files = [target_filename(filename), temporary_file)
           copy(source_filename(filename), target_filename(filename))
     except CancelledException:
        for source_file, temp_file in copied_files:
            copy(temp_file, source_file)
     else:
        for source_file, temp_file in copied_files:
            delete(temp_file)

이제 레지스트리 설정, 프로그램 아이콘 등을 수행해야한다는 논리를 곱하면 상당히 혼란스러워합니다.

귀하의 # 1 솔루션은 다음과 같습니다.

class Installer:
    def install(self):
        try:
            self.copy_new_files()
        except CancellationError:
            self.restore_original_files()
        else:
            self.remove_temp_files()

이렇게하면 전체 알고리즘이 더 명확 해지지 만 서로 다른 부분이 통신하는 방식은 숨겨집니다.

접근법 # 2는 다음과 같습니다.

def install_program():
    try:
       temp_files = copy_new_files()
    except CancellationError as error:
       restore_old_files(error.files_that_were_copied)
    else:
       remove_temp_files(temp_files)

이제는 데이터 조각이 함수간에 어떻게 이동하는지 분명하지만 매우 어색합니다.

이 함수를 어떻게 작성해야합니까?

def install_program():
    with FileTransactionLog() as file_transaction_log:
         copy_new_files(file_transaction_log)

FileTransactionLog 객체는 컨텍스트 관리자입니다. copy_new_files는 파일을 복사 할 때 FileTransactionLog를 통해 파일을 복사하고 임시 사본 작성을 처리하고 복사 된 파일을 추적합니다. 예외 인 경우 원본 파일을 다시 복사하고 성공한 경우 임시 사본을 삭제합니다.

이것은 우리가 작업의 자연스러운 분해를 발견했기 때문에 작동합니다. 이전에는 응용 프로그램 설치 방법에 대한 논리와 취소 된 설치 복구 방법에 대한 논리를 혼합했습니다. 이제 트랜잭션 로그는 임시 파일 및 부기에 대한 모든 세부 정보를 처리하며이 기능은 기본 알고리즘에 중점을 둘 수 있습니다.

사건이 같은 보트에 있다고 생각합니다. 복잡한 작업을보다 간단하고 우아하게 표현할 수 있도록 부기 요소를 일종의 객체로 추출해야합니다.


답변

방법 1의 유일한 단점은 차선의 사용 패턴이기 때문에 가장 좋은 해결책은 캡슐화를 한 단계 더 추진하는 것입니다. 클래스를 사용하고 독립형 함수를 제공하십시오. :

def publicFunction(var1, var2, param1, param2)
    thing = Thing(var1, var2)
    thing.theMethod(param1, param2)

이를 통해 코드에 대한 가능한 가장 작은 인터페이스가 있으며 내부적으로 사용하는 클래스는 실제로 공용 함수의 구현 세부 사항이됩니다. 호출 코드는 내부 클래스에 대해 알 필요가 없습니다.


답변

한편으로, 질문은 어떻게 든 언어에 구애받지 않습니다. 그러나 구현은 언어와 그 패러다임에 달려 있습니다. 이 경우 여러 패러다임을 지원하는 Python입니다.

솔루션 외에도 더 기능적인 방식으로 상태 를 유지 하면서 작업을 완료 할 가능성도 있습니다. 예 :

def outer(param1, param2):
    def inner1(param1, param2, param3):
        pass
    def inner2(param1, param2):
        pass
    return inner2(inner1(param1),param2,param3)

모든 것이

  • 가독성
  • 일관성
  • 유지 보수성

그러나 코드베이스가 OOP 인 경우 갑자기 일부 파트가 (더) 기능적인 스타일로 작성된 경우 일관성을 위반합니다.


답변

코딩 할 수있는 이유는 무엇입니까?

나는 내가 읽은 답변에 반대되는 견해를 제시한다. 이러한 관점에서 모든 대답과 솔직한 질문 자체는 코딩 역학에 중점을 둡니다. 그러나 이것은 디자인 문제입니다.

함수가 복잡하고 변수가 많은 경우 클래스를 만들어야합니까?

예, 디자인에 적합합니다. 클래스 자체 또는 다른 클래스의 일부이거나 동작이 클래스간에 분산 될 수 있습니다.


객체 지향 디자인은 복잡성에 관한 것입니다

OO의 핵심은 시스템 자체 측면에서 코드를 캡슐화하여 크고 복잡한 시스템을 성공적으로 구축하고 유지 관리하는 것입니다. “적절한”디자인은 모든 것이 어떤 클래스에 있다고 말합니다.

OO 디자인은 주로 단일 책임 원칙을 준수하는 집중 수업을 통해 복잡성을 관리합니다. 이 클래스는 시스템의 호흡과 깊이에 걸쳐 구조와 기능을 제공하고 이러한 차원을 상호 작용하고 통합합니다.

이를 감안할 때 시스템에 매달려있는 기능-너무 보편적 인 일반 유틸리티 클래스-은 불충분 한 디자인을 암시하는 코드 냄새라고 종종 말합니다. 나는 동의하는 경향이있다.


답변

표준 Python 라이브러리에 존재하는 것과 요구 사항을 비교 한 다음 어떻게 구현되는지 살펴보십시오.

객체가 필요하지 않은 경우에도 함수 내에서 함수를 정의 할 수 있습니다. 함께 파이썬 3 새가 nonlocal당신이 당신의 부모 함수에서 변수를 변경할 수 있도록 선언.

추상화와 구현 정리를 구현하기 위해 함수 내에 간단한 개인 클래스를 두는 것이 여전히 유용 할 수 있습니다.


답변