문제

객체 지향 프로그래밍의 가장 큰 장점 중 하나는 캡슐화이며, 우리가 (또는 적어도 내가) 배운 "진실" 중 하나는 멤버는 항상 비공개로 설정되어야 하며 접근자와 변경자를 통해 사용할 수 있어야 한다는 것입니다. 방법을 통해 변경 사항을 확인하고 검증할 수 있는 능력을 보장합니다.

하지만 이것이 실제로 얼마나 중요한지 궁금합니다.특히, 더 복잡한 멤버(컬렉션 등)가 있는 경우 컬렉션의 키를 가져오고 컬렉션에서 항목을 추가/제거하는 여러 메서드를 만드는 대신 이를 공개로 만드는 것이 매우 유혹적일 수 있습니다. 등.

당신은 일반적으로 규칙을 따르나요?자신을 위해 작성된 코드인지 아니면 자신을 위해 작성된 코드인지에 따라 답이 바뀌나요?다른 사람이 사용하려고?이 난독화로 인해 내가 놓친 더 미묘한 이유가 있습니까?

도움이 되었습니까?

해결책

때에 따라 다르지.이는 실용적으로 결정해야 할 문제 중 하나입니다.

포인트를 표현하는 클래스가 있다고 가정해 보겠습니다.X 및 Y 좌표에 대한 getter 및 setter를 가질 수도 있고, 둘 다 공개하고 데이터에 대한 무료 읽기/쓰기 액세스를 허용할 수도 있습니다.제 생각에는 클래스가 일부 유용한 함수가 첨부된 데이터 컬렉션인 영광스러운 구조체처럼 작동하기 때문에 괜찮습니다.

그러나 내부 데이터에 대한 전체 액세스 권한을 제공하지 않고 클래스에서 제공하는 메서드에 의존하여 객체와 상호 작용하는 상황이 많이 있습니다.예를 들어 HTTP 요청 및 응답이 있습니다.이 경우 누구든지 유선을 통해 무엇이든 보낼 수 있도록 허용하는 것은 좋지 않습니다. 클래스 메서드에 의해 처리되고 형식이 지정되어야 합니다.이 경우 클래스는 단순한 데이터 저장소가 아닌 실제 개체로 간주됩니다.

이는 실제로 동사(메서드)가 구조를 구동하는지 여부 또는 데이터가 구동하는지 여부에 달려 있습니다.

다른 팁

과거에 많은 사람들이 작업한 몇 년 된 코드를 유지해야 하는 사람으로서, 멤버 속성이 공개되면 결국 남용된다는 것이 매우 분명합니다.사람들이 접근자와 돌연변이자 개념에 동의하지 않는다는 이야기도 들었습니다. "클래스의 내부 작동을 숨기는" 캡슐화의 목적에 아직 부응하지 못하기 때문입니다.분명히 논란의 여지가 있는 주제이지만 내 의견은 "모든 멤버 변수를 비공개로 만들고 클래스가 무엇을 해야 하는지에 대해 주로 생각하세요"입니다. 하다 (방법)보다는 어떻게 사람들이 내부 변수를 변경하도록 허용할 것입니다."

예, 캡슐화가 중요합니다.기본 구현을 노출하면 (적어도) 두 가지 문제가 발생합니다.

  1. 책임을 뒤섞습니다.호출자는 기본 구현을 이해할 필요가 없거나 이해할 필요가 없습니다.그들은 단지 수업이 그 일을 하기를 원해야 합니다.기본 구현을 노출하면 클래스가 해당 작업을 수행하지 않습니다.대신, 호출자에게 책임을 전가하는 것뿐입니다.
  2. 기본 구현에 연결됩니다.기본 구현을 노출하면 해당 구현에 연결됩니다.예를 들어 아래에 컬렉션이 있다고 호출자에게 알리면 새 구현을 위해 컬렉션을 쉽게 교체할 수 없습니다.

이러한 (및 기타) 문제는 기본 구현에 직접 액세스할 수 있는지 또는 모든 기본 메서드를 복제하는지 여부에 관계없이 적용됩니다.필요한 구현만 노출해야 하며 그 이상은 아닙니다.구현을 비공개로 유지하면 전체 시스템의 유지 관리가 더 쉬워집니다.

나는 가능한 한 오랫동안 멤버를 비공개로 유지하고 동일한 클래스 내에서도 getter를 통해서만 멤버에 액세스하는 것을 선호합니다.또한 가능한 한 값 스타일 개체를 홍보하기 위해 첫 번째 초안으로 setter를 피하려고 노력합니다.종속성 주입 작업을 많이 하면 setter는 있지만 getter는 없는 경우가 많습니다. 클라이언트는 객체를 구성할 수 있어야 하지만 (다른 사용자는) 이것이 구현 세부 사항이므로 실제로 구성된 것이 무엇인지 알 수 없기 때문입니다.

안부, Ollie

나는 내 자신의 코드일지라도 규칙을 매우 엄격하게 따르는 경향이 있습니다.그런 이유로 저는 C#의 속성을 정말 좋아합니다.주어진 값을 제어하기가 정말 쉽지만 여전히 변수로 사용할 수 있습니다.또는 세트를 비공개로 설정하고 공개로 설정하는 등의 작업을 수행합니다.

기본적으로 정보 숨기기는 코드 명확성에 관한 것입니다.이는 다른 사람이 여러분의 코드를 더 쉽게 확장할 수 있도록 하고, 여러분 클래스의 내부 데이터로 작업할 때 실수로 버그를 생성하는 것을 방지하도록 설계되었습니다.이라는 원칙에 기초하고 있습니다. 아무도 댓글을 읽지 않습니다, 특히 지침이 포함된 것입니다.

예: 저는 변수를 업데이트하는 코드를 작성 중이며 Gui가 변경 사항을 반영하도록 변경되는지 절대적으로 확인해야 합니다. 가장 쉬운 방법은 데이터를 업데이트하는 대신 호출되는 접근자 메서드(일명 "Setter")를 추가하는 것입니다. 업데이트됩니다.

해당 데이터를 공개하고 Setter 메서드를 거치지 않고 변수를 변경하는 경우(이는 욕설을 할 때마다 발생함) 업데이트가 표시되지 않는 이유를 알아내기 위해 디버깅하는 데 한 시간을 소비해야 합니다.데이터를 "가져오는" 경우에도 동일하게 적용됩니다.헤더 파일에 주석을 넣을 수 있지만 뭔가 심각하게 잘못될 때까지 아무도 그것을 읽지 않을 가능성이 높습니다.비공개로 시행한다는 것은 실수가 발생한다는 것을 의미합니다. 캔트 런타임 버그가 아닌 쉽게 찾을 수 있는 컴파일 타임 버그로 나타나기 때문입니다.

경험상 멤버 변수를 공개로 만들고 Getter 및 Setter 메서드를 생략하려는 유일한 경우는 변수를 변경해도 부작용이 없다는 점을 확실히 밝히려는 경우입니다.특히 두 변수를 한 쌍으로 보유하는 클래스처럼 데이터 구조가 단순한 경우에는 더욱 그렇습니다.

이는 일반적으로 발생하는 매우 드문 일입니다. 원하다 부작용이 있으며, 생성 중인 데이터 구조가 너무 단순해서 그렇지 않은 경우(예: 페어링) 이미 표준 라이브러리에 더 효율적으로 작성된 데이터 구조가 있을 것입니다.

즉, 대학에서 받는 프로그램과 같이 확장이 필요 없고 일회용인 대부분의 작은 프로그램의 경우 이는 무엇보다 "좋은 습관"입니다. 왜냐하면 작성하는 동안 기억할 것이기 때문입니다. 건네주고 다시는 코드를 건드리지 않을 거예요.또한 릴리스 코드가 아닌 데이터를 저장하는 방법을 알아내는 방법으로 데이터 구조를 작성하는 경우 Getter 및 Setter가 도움이 되지 않으며 학습 경험에 방해가 될 것이라는 좋은 주장이 있습니다. .

다른 사람이 작성한 개체 및 구조에 의해 코드가 호출될 가능성이 있는 작업장이나 대규모 프로젝트에 도착할 때만 이러한 "알림"을 강력하게 만드는 것이 중요합니다.한 사람의 프로젝트인지 아닌지는 놀랍게도 중요하지 않습니다. "지금부터 6주 후의 당신"은 동료와 마찬가지로 다른 사람이라는 단순한 이유 때문입니다.그리고 "6주 전의 나"는 종종 게으른 것으로 드러납니다.

마지막 요점은 일부 사람들은 정보 숨기기에 매우 열성적이어서 데이터가 불필요하게 공개되면 짜증을 낼 것이라는 점입니다.그들을 유머러스하게 만드는 것이 가장 좋습니다.

C# 속성은 공개 필드를 '시뮬레이트'합니다.꽤 멋져 보이고 구문이 실제로 get/set 메소드 생성 속도를 높여줍니다.

객체에 대한 메서드 호출의 의미를 염두에 두세요.메서드 호출은 컴파일러나 런타임 시스템에서 다양한 방식으로 구현할 수 있는 매우 높은 수준의 추상화입니다.

호출하는 메서드인 개체가 동일한 프로세스/메모리 맵에 존재하는 경우 컴파일러나 VM을 통해 메서드를 최적화하여 데이터 멤버에 직접 액세스할 수 있습니다.반면에 개체가 분산 시스템의 다른 노드에 있는 경우 해당 개체의 내부 데이터 멤버에 직접 액세스할 수 있는 방법은 없지만 메시지를 보내면서 해당 메서드를 계속 호출할 수 있습니다.

인터페이스를 코딩하면 대상 객체가 어디에 있는지, 해당 객체의 메서드가 어떻게 호출되는지, 심지어 동일한 언어로 작성되었는지 상관하지 않는 코드를 작성할 수 있습니다.


컬렉션의 모든 메서드를 구현하는 개체의 예에서 해당 개체는 실제로 ~이다 컬렉션.따라서 아마도 상속이 캡슐화보다 나은 경우가 될 것입니다.

그것은 당신이 그들에게 제공한 것으로 사람들이 무엇을 할 수 있는지를 통제하는 것에 관한 것입니다.더 많이 통제할수록 더 많은 가정을 할 수 있습니다.

또한 이론적으로는 기본 구현 등을 변경할 수 있지만 대부분의 경우 다음과 같습니다.

private Foo foo;
public Foo getFoo() {}
public void setFoo(Foo foo) {}

정당화하기가 조금 어렵습니다.

캡슐화는 다음 중 하나 이상이 유지될 때 중요합니다.

  1. 당신을 제외한 누구든지 당신의 클래스를 사용할 것입니다(또는 문서를 읽지 않기 때문에 당신의 불변성을 깨뜨릴 것입니다).
  2. 문서를 읽지 않는 사람은 누구나 귀하의 클래스를 사용할 것입니다(또는 신중하게 문서화된 불변성을 깨뜨릴 것입니다).이 범주에는 지금부터 2년 후의 당신도 포함됩니다.
  3. 미래의 어느 시점에 누군가가 여러분의 클래스에서 상속받게 될 것입니다(왜냐하면 필드의 값이 변경되면 추가 작업을 수행해야 하므로 setter가 있어야 하기 때문입니다).

그것이 나만을 위한 것이고 몇몇 장소에서 사용되고 내가 그것으로부터 상속받지 않을 것이며 필드를 변경해도 클래스가 가정하는 불변성이 무효화되지 않는 경우, 그때야 가끔 필드를 공개하겠습니다.

내 경향은 가능하면 모든 것을 비공개로 설정하려고 노력하는 것입니다.이렇게 하면 객체 경계가 최대한 명확하게 정의되고 객체가 최대한 분리된 상태로 유지됩니다.처음(두 번째, 다섯 번째?) 번에 실수한 개체를 다시 작성해야 할 때 더 적은 수의 개체에 대한 피해를 유지하기 때문에 이 방법이 마음에 듭니다.

개체를 충분히 단단하게 결합한 경우 개체를 하나의 개체로 결합하는 것이 더 간단할 수 있습니다.결합 제약 조건을 충분히 완화하면 구조화된 프로그래밍으로 돌아갈 수 있습니다.

객체 묶음이 단지 접근자 함수일 뿐이라는 것을 알게 되면 객체 구분을 다시 생각해야 할 수도 있습니다.해당 데이터에 대해 어떤 작업도 수행하지 않으면 해당 데이터는 다른 개체의 일부로 속할 수 있습니다.

물론, 라이브러리와 같은 것을 작성하는 경우 다른 사람이 프로그래밍할 수 있도록 가능한 한 명확하고 선명한 인터페이스를 원합니다.

작업에 도구를 맞추세요...최근에 현재 코드베이스에서 다음과 같은 코드를 보았습니다.

private static class SomeSmallDataStructure {
    public int someField;
    public String someOtherField;
}

그리고 이 클래스는 여러 데이터 값을 쉽게 전달하기 위해 내부적으로 사용되었습니다.항상 의미가 있는 것은 아니지만 메서드 없이 DATA만 있고 이를 클라이언트에 노출하지 않는 경우 매우 유용한 패턴이라고 생각합니다.

내가 사용한 가장 최근의 용도는 상단에 선언적으로 정의된 데이터 테이블이 표시되는 JSP 페이지였습니다.따라서 처음에는 데이터 필드당 하나의 배열로 여러 배열에 있었습니다.이로 인해 함께 표시되는 정의에서 필드가 서로 옆에 있지 않아 코드를 살펴보기가 다소 어려워졌습니다...그래서 위와 같이 이를 하나로 묶는 간단한 클래스를 만들었습니다.그 결과는 이전보다 훨씬 더 읽기 쉬운 코드였습니다.

도의적인...때때로 코드를 더 간단하고 읽기 쉽게 만들 수 있다면 "허용되는 나쁜" 대안을 고려해야 합니다. 충분히 생각하고 결과를 고려하는 한...당신이 듣는 모든 것을 맹목적으로 받아들이지 마십시오.

그 말은...공개 getter 및 setter는 공개 필드와 거의 동일합니다.적어도 본질적으로(좀 더 유연성이 있지만 모든 필드에 적용하는 것은 여전히 ​​나쁜 패턴입니다).

Java 표준 라이브러리에도 다음과 같은 경우가 있습니다. 공개 필드.

내가 사물을 의미있게 만들 때 그것은 더 쉽게 에게 사용 그리고 더 쉽게 유지하다.

예를 들어:Person.Hand.Grab(얼마나 빨리, 얼마나 빨리);

비결은 멤버를 단순한 값이 아니라 객체 자체로 생각하는 것입니다.

나는 이 질문이 캡슐화의 개념과 '정보 은닉'을 혼동한다고 주장하고 싶습니다.
('캡슐화'라는 개념에 대한 일반적인 해석과 일치하는 것 같기 때문에 이것은 비판이 아닙니다.)

그러나 나에게 '캡슐화'는 다음 중 하나입니다.

  • 여러 항목을 컨테이너로 다시 그룹화하는 프로세스
  • 컨테이너 자체가 항목을 재그룹화

당신이 납세자 시스템을 설계한다고 가정해보자.납세자마다 다음을 수행할 수 있습니다. 캡슐화하다 의 개념 어린이 ~ 안으로

  • 아이들을 대표하는 아이들의 목록
  • 서로 다른 부모의 자녀를 고려하는 지도
  • 필요한 정보(예: 총 어린이 수)를 제공하는 객체 Children(Child가 아님)

여기에는 세 가지 종류의 캡슐화가 있습니다. 2개는 하위 수준 컨테이너(목록 또는 맵)로 표시되고, 하나는 객체로 표시됩니다.

그러한 결정을 내림으로써 당신은

  • 해당 캡슐화를 공개, 보호 또는 비공개로 설정합니다.'정보 은닉'의 선택은 아직 남아있다
  • 완전한 추상화를 수행합니다(Children 개체의 속성을 구체화해야 하며 납세자 시스템의 관점에서 관련 정보만 유지하는 Child 개체를 생성하기로 결정할 수도 있음).
    추상화는 객체의 어떤 속성이 시스템과 관련되고 완전히 무시되어야 하는지를 선택하는 프로세스입니다.

내 요점은 다음과 같습니다.
해당 질문의 제목은 다음과 같습니다.
개인 대.실제로 공개 회원(얼마나 중요한지 정보 숨기기?)

하지만 내 2 센트에 불과합니다.나는 캡슐화를 '정보 은닉' 결정을 포함한 프로세스로 간주할 수 있다는 점을 전적으로 존중합니다.

그러나 나는 항상 '추상화' - '캡슐화' - '정보 은폐 또는 가시성'을 구별하려고 노력합니다.

@VonC

국제표준화기구(International Organization for Standardization)의 "개방형 분산 처리 참조 모델"이라는 흥미로운 내용을 읽어보실 수 있습니다.이는 다음을 정의합니다."캡슐화:객체에 포함된 정보는 객체가 지원하는 인터페이스에서의 상호 작용을 통해서만 액세스할 수 있다는 속성입니다."

나는 여기서 정보 은닉이 이 정의의 중요한 부분임을 입증하려고 노력했습니다.http://www.edmundkirwan.com/encap/s2.html

문안 인사,

에드.

나는 많은 getter와 setter가 코드 냄새 프로그램의 구조가 잘 설계되지 않았다는 것입니다.해당 getter 및 setter를 사용하는 코드를 살펴보고 실제로 클래스에 포함되어야 하는 기능을 찾아야 합니다.대부분의 경우 클래스의 필드는 비공개 구현 세부 정보여야 하며 해당 클래스의 메서드만 이를 조작할 수 있습니다.

getter와 setter를 모두 갖는 것은 필드가 공개되는 것과 같습니다(getter와 setter가 사소하거나 자동으로 생성되는 경우).때로는 다형성이 필요하지 않거나 프레임워크에 get/set 메서드가 필요한 경우(프레임워크를 변경할 수 없는 경우)가 아니면 코드가 더 단순해지도록 필드를 공개로 선언하는 것이 더 나을 수도 있습니다.

그러나 getter와 setter를 갖는 것이 좋은 패턴인 경우도 있습니다.한 가지 예:

애플리케이션의 GUI를 생성할 때 한 클래스(FooModel)에서 GUI의 동작을 유지하여 쉽게 단위 테스트할 수 있도록 하고, 테스트할 수 있는 다른 클래스(FooView)에서 GUI의 시각화를 갖도록 노력합니다. 수동으로만.뷰와 모델은 간단한 글루 코드로 결합됩니다.사용자가 필드 값을 변경할 때 x, 뷰 호출 setX(String) 그러면 모델의 다른 부분이 변경되었다는 이벤트가 발생할 수 있으며 뷰는 getter를 사용하여 모델에서 업데이트된 값을 가져옵니다.

한 프로젝트에는 15개의 getter와 setter가 있는 GUI 모델이 있는데, 그 중 3개의 get 메서드만 (IDE가 생성할 수 있는) 사소한 것입니다.다른 모든 항목에는 다음과 같은 일부 기능이나 중요하지 않은 표현이 포함되어 있습니다.

public boolean isEmployeeStatusEnabled() {
    return pinCodeValidation.equals(PinCodeValidation.VALID);
}

public EmployeeStatus getEmployeeStatus() {
    Employee employee;
    if (isEmployeeStatusEnabled()
            && (employee = getSelectedEmployee()) != null) {
        return employee.getStatus();
    }
    return null;
}

public void setEmployeeStatus(EmployeeStatus status) {
    getSelectedEmployee().changeStatusTo(status, getPinCode());
    fireComponentStateChanged();
}

실제로 나는 항상 "모든 것에 맞는 크기는 없다"라는 단 하나의 규칙만을 따릅니다.

캡슐화와 그 중요성은 프로젝트의 산물입니다.인터페이스에 액세스하는 개체는 무엇이며 인터페이스를 어떻게 사용합니까? 멤버에 대한 불필요한 액세스 권한이 있는지 여부가 중요합니까?각 프로젝트 구현 작업을 수행할 때 스스로에게 물어봐야 하는 질문 등이 있습니다.

나는 모듈 내 강령의 깊이에 따라 결정을 내립니다.모듈 내부에 있고 외부 세계와 인터페이스하지 않는 코드를 작성하는 경우 프로그래머 성능(코드를 얼마나 빨리 작성하고 다시 작성할 수 있는지)에 영향을 미치기 때문에 private으로 캡슐화하지 않습니다.

그러나 사용자 코드가 포함된 모듈의 인터페이스 역할을 하는 개체의 경우 엄격한 개인 정보 보호 패턴을 준수합니다.

확실히 내부 코드를 작성하는지 아니면 다른 사람이 사용할 코드인지(또는 자신이 포함된 단위로 사용하는지) 차이가 있습니다. 외부에서 사용되는 모든 코드에는 잘 정의되고 문서화된 인터페이스가 있어야 합니다. 가능한 한 적게 바꾸고 싶습니다.

내부 코드의 경우 난이도에 따라 지금은 간단한 방식으로 작업하는 것이 작업이 덜하고 나중에 약간의 페널티를 지불할 수도 있습니다.물론 머피의 법칙은 캡슐화하지 못한 클래스의 내부를 변경해야 하는 경우 나중에 광범위한 변경을 수행해야 할 때 단기 이득이 여러 번 지워지는 것을 보장합니다.

특히 반환할 컬렉션을 사용하는 예에서 이러한 컬렉션의 구현이 변경되어(단순한 멤버 변수와는 달리) 캡슐화의 유용성이 높아질 가능성이 있는 것 같습니다.

즉, 나는 Python의 처리 방식을 좋아합니다.멤버 변수는 기본적으로 공개됩니다.이를 숨기거나 유효성 검사를 추가하려는 경우 제공되는 기술이 있지만 이는 특수한 경우로 간주됩니다.

나는 거의 항상 이에 대한 규칙을 따릅니다.나에게는 네 가지 시나리오가 있습니다. 기본적으로 규칙 자체와 몇 가지 예외(모두 Java의 영향을 받음)입니다.

  1. getter/setter를 통해 액세스되는 현재 클래스 외부의 모든 항목에서 사용 가능
  2. 내부-클래스 사용법은 일반적으로 메소드 매개변수가 아님을 분명히 하기 위해 'this'가 앞에 붙습니다.
  3. 운송 개체처럼 극도로 작은 상태를 유지하는 것을 의미합니다. 기본적으로 속성의 직선 샷입니다.모든 공개
  4. 일종의 확장을 위해서는 비공개가 되어야 합니다.

여기에는 대부분의 기존 답변으로 해결되지 않는 실질적인 문제가 있습니다.캡슐화와 깨끗하고 안전한 인터페이스를 외부 코드에 노출하는 것은 항상 훌륭하지만 작성 중인 코드가 공간적 및/또는 시간적으로 큰 "사용자" 기반에서 사용되도록 의도된 경우 훨씬 더 중요합니다.내 말은, 누군가(심지어 당신도)가 미래에도 코드를 유지 관리하도록 계획하거나 소수 이상의 다른 개발자의 코드와 인터페이스할 모듈을 작성하는 경우 훨씬 더 많이 생각해야 한다는 것입니다. 일회성이거나 전적으로 본인이 작성한 코드를 작성하는 것보다 신중하게 작업하세요.

솔직히, 나는 이것이 비참한 소프트웨어 엔지니어링 관행이라는 것을 알고 있지만 처음에는 모든 것을 공개하여 기억하고 입력하는 것이 약간 더 빨라지고 이해가 되면 캡슐화를 추가하는 경우가 많습니다.요즘 가장 인기 있는 IDE의 리팩토링 도구를 사용하면 어떤 접근 방식을 사용할지 결정됩니다(캡슐화 추가 vs.제거) 예전보다 관련성이 훨씬 떨어집니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top