C #에서 추상 클래스를 정의로 사용


21

C ++ 개발자는 C ++ 헤더 파일에 익숙하며 코드 내에 일종의 "문서화"를하는 것이 유리합니다. 나는 보통 그 때문에 C # 코드를 읽어야 할 때 나쁜 시간을 보낸다. 내가 작업하는 클래스에 대한 그런 정신지도가 없다.

소프트웨어 엔지니어로서 프로그램의 프레임 워크를 설계한다고 가정 해 봅시다. C ++ 헤더로하는 것과 비슷하게 모든 클래스를 추상 구현되지 않은 클래스로 정의하고 개발자가이를 구현하게하는 것은 너무 미치 겠는가?

누군가가 이것이 끔찍한 해결책이라고 생각할 수있는 몇 가지 이유가 있다고 생각하지만 그 이유는 확실하지 않습니다. 이와 같은 솔루션을 위해 고려해야 할 것은 무엇입니까?


13
C #은 C ++과 완전히 다른 프로그래밍 언어입니다. 일부 구문은 비슷해 보이지만 제대로 작동하려면 C #을 이해해야합니다. 그리고 헤더 파일에 의존하는 나쁜 습관에 대해 뭔가를해야합니다. 나는 수십 년 동안 C ++로 일해 왔으며 헤더 파일을 읽지 않고 작성했습니다.
구부러진

17
C #과 Java의 가장 좋은 것 중 하나는 헤더 파일이 없다는 것입니다! 좋은 IDE를 사용하십시오.
Erik Eidt

9
C ++ 헤더 파일의 선언은 생성 된 이진의 일부가되지 않습니다. 그것들은 컴파일러와 링커를 위해 존재합니다. 이 C # 추상 클래스는 생성 된 코드의 일부이므로 아무런 이점이 없습니다.
Mark Benningfield

16
C #의 해당 구문은 추상 클래스가 아닌 인터페이스입니다.
Robert Harvey

6
@DaniBarcaCasafont "헤더는 클래스 메소드와 속성이 무엇인지, 어떤 매개 변수 유형이 예상되고 어떤 것이 반환되는지를 확인하는 빠르고 안정적인 방법입니다." Visual Studio를 사용할 때 내 대안은 Ctrl-MO 단축키입니다.
wha7ever-복직 모니카

답변:


44

이것이 C ++에서 수행 된 이유는 컴파일러를보다 빠르고 쉽게 구현하는 것과 관련이있었습니다. 이것은 프로그래밍을 쉽게하기 위한 디자인이 아닙니다 .

헤더 파일의 목적은 컴파일러가 예상되는 모든 함수 이름을 알고 메모리 위치를 할당하기 위해 컴파일러가 초고속 첫 번째 패스를 수행 할 수 있도록하는 것입니다. 아직 파싱되지 않았습니다.

최신 개발 환경에서 오래된 하드웨어 제한의 결과를 복제하려고 시도하지 않는 것이 좋습니다!

모든 클래스에 대해 인터페이스 또는 추상 클래스를 정의하면 생산성이 저하됩니다. 그 시간으로 무엇을 할 수 있었 습니까? 또한 다른 개발자는이 규칙을 따르지 않습니다.

실제로 다른 개발자가 추상 클래스를 삭제할 수도 있습니다. 이 두 가지 기준을 모두 충족하는 코드에서 인터페이스를 찾으면이를 삭제하고 코드에서 리팩터링합니다.1. Does not conform to the interface segregation principle 2. Only has one class that inherits from it

다른 하나는 Visual Studio에 자동으로 달성하려는 작업을 수행하는 도구가 포함되어 있다는 것입니다.

  1. 그만큼 Class View
  2. 그만큼 Object Browser
  3. 에서 Solution Explorer당신의 기능, 매개 변수 및 반환 형식을 볼 수 클래스를 소비하는 삼각형 클릭 할 수 있습니다.
  4. 파일 탭 아래에 세 개의 작은 드롭 다운 메뉴가 있으며 가장 오른쪽에있는 메뉴는 현재 클래스의 모든 멤버를 나열합니다.

C #에서 C ++ 헤더 파일을 복제하는 데 시간을 투자하기 전에 위의 시도를 해보십시오.


또한 이것을 수행하지 않는 기술적 이유가 있습니다 ... 최종 바이너리를 필요 이상으로 크게 만듭니다. Mark Benningfield의이 의견을 반복하겠습니다.

C ++ 헤더 파일의 선언은 생성 된 이진의 일부가되지 않습니다. 그것들은 컴파일러와 링커를 위해 존재합니다. 이 C # 추상 클래스는 생성 된 코드의 일부이므로 아무런 이점이 없습니다.


또한 Robert Harvey는 기술적으로 C #에서 가장 가까운 헤더는 추상 클래스가 아닌 인터페이스입니다.


2
또한 불필요한 가상 디스패치 호출이 많이 발생합니다 (코드가 성능에 민감한 경우 좋지 않습니다).
루카스 Trzesniewski

5
여기에 인터페이스 분리 원칙 이 있습니다.
NH.

2
전체 클래스를 간단히 축소 (오른쪽 클릭-> 개요-> 정의로 축소)하여 조감도를 볼 수도 있습니다.
jpmc26

"그 당시에 무엇을 할 수 있었나?"-> 음, 복사하여 붙여 넣기 그리고 아주 사소한 포스트 워크? 약 3 초가 걸립니다. 물론 그 대신 담배를 굴릴 준비를 위해 필터 팁을 꺼낼 수있었습니다. 실제로는 관련이 없습니다. [면책 조항 : 관용적 C ++뿐만 아니라 관용적 C ++ 및 더 많은 언어를
좋아

1
@ phresnel : 클래스 정의를 반복하고 3의 추상 클래스에서 원래 클래스를 상속하려고 시도하고 오류가없는 것으로 확인되어 오류가없는 것으로 확인되었습니다. 그것은 OP의 제안 된 유스 케이스이므로 복잡한 클래스를 복잡하게 만드는 것 같습니다. 본질적으로 원래 클래스의 복잡한 특성은 추상 클래스 정의를 마음으로 작성할 수 없다는 것을 의미합니다 (이미 마음으로 알고 있다는 것을 의미하기 때문에) 전체 코드베이스에 대해이 작업을 수행하는 데 필요한 시간을 고려하십시오 .
Flater

14

첫째, 순전히 추상 클래스는 실제로 여러 상속을 수행 할 수없는 인터페이스라는 것을 이해하십시오.

쓰기 클래스, 추출 인터페이스는 두뇌 활동입니다. 우리는 리팩토링을 할 수 있습니다. 동정입니다. 이 "모든 클래스는 인터페이스를 얻는다"패턴을 따르면 혼란을 야기 할뿐만 아니라 요점을 완전히 놓친다.

인터페이스는 단순히 클래스가 할 수있는 모든 것을 공식적으로 설명하는 것으로 생각해서는 안됩니다. 인터페이스는 필요한 클라이언트 코드를 사용하여 부과 된 계약으로 간주해야합니다.

현재 하나의 클래스 만 구현하는 인터페이스를 작성하는 데 전혀 문제가 없습니다. 실제로 클래스가 아직 구현하지 않은 경우 실제로 상관하지 않습니다. 사용하는 코드에 무엇이 필요한지 생각하기 때문입니다. 인터페이스는 사용 코드가 요구하는 것을 표현합니다. 나중에 오는 것은 이러한 기대를 만족하는 한 좋아하는 것을 할 수 있습니다.

한 객체가 다른 객체를 사용할 때마다이 작업을 수행하지 않습니다. 경계를 넘을 때이 작업을 수행합니다. 하나의 물체가 어떤 다른 물체와 대화하고 있는지 정확히 알고 싶지 않을 때 수행합니다. 다형성이 작동하는 유일한 방법입니다. 클라이언트 코드가 말하는 객체가 변경 될 것으로 예상되면 그렇게합니다. 내가 사용하는 것이 String 클래스 일 때 확실히하지 않습니다. String 클래스는 훌륭하고 안정적이며 변경되는 것을 막을 필요가 없습니다.

추상화 대신 구체적인 구현과 직접 상호 작용하기로 결정하면 구현이 변경되지 않을 것으로 믿을만큼 충분히 안정적임을 예측할 수 있습니다.

바로 의존성 역전 원칙을 강화하는 방법이 있습니다. 맹목적으로 이것을 모든 것에 적용해서는 안됩니다. 추상화를 추가하면 실제로 프로젝트 수명 동안 안정적으로 클래스를 구현하는 선택을 신뢰하지 않는다고 말하는 것입니다.

이것은 모두 공개 폐쇄 원칙 을 따르려고한다고 가정합니다 . 이 원칙은 확립 된 코드를 직접 변경하는 것과 관련된 비용이 상당 할 경우에만 중요합니다. 사람들이 객체를 분리하는 것이 얼마나 중요한지에 동의하지 않는 주된 이유 중 하나는 직접적인 변경을 할 때 모든 사람이 동일한 비용을 경험하지 않기 때문입니다. 전체 코드베이스를 다시 테스트, 재 컴파일 및 재배포하는 것이 쉽지 않은 경우 직접 수정하여 변경해야 할 필요성을 해결하는 것이이 문제를 매우 매력적으로 단순화 할 수 있습니다.

이 질문에 대한 간단한 답변은 없습니다. 인터페이스 또는 추상 클래스는 모든 클래스에 추가해야하는 것이 아니며 구현 클래스 수를 세어 필요하지 않다고 결정할 수는 없습니다. 변화를 다루는 것입니다. 미래를 예상한다는 의미입니다. 잘못 이해해도 놀라지 마십시오. 구석에 자신을 백업하지 않고 최대한 간단하게 유지하십시오.

코드를 읽는 데 도움이되는 추상화를 작성하지 마십시오. 이를위한 도구가 있습니다. 디커플링이 필요한 것을 분리하려면 추상화를 사용하십시오.


5

그렇습니다. (1) 불필요한 코드를 소개합니다. (2) 독자를 혼란스럽게 할 것입니다.

C #으로 프로그래밍하려면 C #을 읽는 데 익숙해 져야합니다. 다른 사람들이 작성한 코드는 어쨌든이 패턴을 따르지 않습니다.


1

단위 테스트

인터페이스 또는 추상 클래스로 코드를 어지럽히 지 않고 단위 테스트를 작성하는 것이 좋습니다 (다른 이유로 보증되는 경우 제외).

잘 작성된 단위 테스트는 클래스의 인터페이스 (헤더 파일, 추상 클래스 또는 인터페이스처럼)를 설명 할뿐만 아니라 원하는 기능을 예를 들어 설명합니다.

예 : 헤더 파일 myclass.h를 다음과 같이 작성했을 수 있습니다.

class MyClass
{
public:
  void foo();
};

대신 C #에서 다음과 같은 테스트를 작성하십시오.

[TestClass]
public class MyClassTests
{
    [TestMethod]
    public void MyClass_should_have_method_Foo()
    {
        //Arrange
        var myClass = new MyClass();
        //Act
        myClass.Foo();
        //Verify
        Assert.Inconclusive("TODO: Write a more detailed test");
    }
}

이 매우 간단한 테스트는 헤더 파일과 동일한 정보를 전달합니다. (매개 변수가없는 함수 "Foo"를 가진 "MyClass"라는 클래스가 있어야합니다.) 헤더 파일은 크기가 작지만 테스트에 훨씬 더 많은 정보가 포함되어 있습니다.

주의 사항 : TDD와 같은 방법론과의 충돌을 다른 개발자가 심각하게 해결하기 위해 선임 소프트웨어 엔지니어 공급 (실패) 테스트를 수행하는 프로세스이지만, 귀하의 경우에는 크게 개선 될 것입니다.


인터페이스없이 단위 테스트 (절연 테스트) = Mocks가 필요합니까? 실제 구현에서 조롱을 지원하는 프레임 워크는 무엇보다도 인터페이스를 사용하여 구현을 모의 구현으로 교체합니다.
Bulan

OP : s 목적에는 모의가 반드시 필요하다고 생각하지 않습니다. 이것은 TDD의 도구로서의 단위 테스트가 아니라 헤더 파일의 대체로서의 단위 테스트입니다. (물론 모의 등을 이용한 "일반적인"단위 테스트로 발전 할 수도있다)
Guran

좋아, OP 측면 만 고려하면 더 잘 이해합니다. 보다 일반적인 적용 답변으로 답변을 읽으십시오. 설명을위한 Thx!
Bulan

@Bulan 광범위한 조롱의 필요성은 종종 나쁜 디자인을 나타냅니다
TheCatWhisperer

@TheCatWhisperer 네, 잘 알고 있습니다. 어떤 논증도 없습니다. 어디에서나 그 문장을 어디에서나 언급하지 못했습니다 : DI는 일반적으로 테스트에 대해 이야기하고있었습니다. 실제 구현 및 인터페이스가없는 경우 모의 방법에 대한 다른 기술이 있는지 확인하십시오.
Bulan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.