생성하는 동안 변경 가능하지만 나중에 변경할 수없는 멤버가있는 클래스


22

객체 컬렉션을 만드는 알고리즘이 있습니다. 이 객체는 생성 과정에서 거의 시작되지 않기 때문에 변경이 가능하지만 알고리즘 내의 다른 위치에 데이터가 채워집니다.

알고리즘이 완료된 후에는 객체를 변경해서는 안되지만 소프트웨어의 다른 부분에서 사용됩니다.

이러한 시나리오에서 아래에 설명 된대로 두 가지 버전의 클래스를 갖는 것이 좋은 방법으로 간주됩니까?

  • 변경 가능한 것은 알고리즘에 의해 생성 된 다음
  • 알고리즘이 완료되면 데이터가 변경 불가능한 객체로 복사되어 반환됩니다.

3
문제 / 질문이 무엇인지 명확히하기 위해 질문을 편집 할 수 있습니까?
Simon Bergot

답변:


46

빌더 패턴을 사용할 수 있습니다 . 필요한 데이터를 수집 할 목적으로 별도의 '빌더'오브젝트를 사용하며 모든 데이터가 수집 될 때 실제 오브젝트를 작성합니다. 작성된 오브젝트는 변경할 수 없습니다.


귀하의 솔루션은 내 요구 사항에 맞는 솔루션이었습니다 (모두 언급되지는 않았습니다). 조사 결과 반환 된 객체가 모두 동일한 특성을 갖지는 않습니다. 빌더 클래스에는 널 입력 가능 필드가 많이 있으며 데이터가 빌드되면 생성 할 세 가지 클래스 중 하나를 선택합니다. 상속에 의해 서로 관련됩니다.
폴 리차드

2
@Paul이 경우,이 답변으로 문제가 해결되면 승인 된 것으로 표시해야합니다.
Riking

24

이를 달성하는 간단한 방법은 속성을 읽을 수있는 인터페이스와 읽기 전용 메서드 및 해당 인터페이스를 구현하는 클래스 만 호출하여 해당 클래스를 작성할 수있는 인터페이스를 갖는 것입니다.

그것을 생성하고 전자를 처리 한 다음 후자를 반환하여 읽기 전용 인터페이스 만 제공합니다. 이를 위해서는 복사가 필요하지 않으며 작성자와 달리 발신자가 사용할 수있는 동작을 쉽게 미세 조정할 수 있습니다.

이 예제를 보자 :

public interface IPerson 
{
    public String FirstName 
    {
        get;
    }

    public String LastName 
    {
        get;
    }
} 

public class PersonImpl : IPerson 
{
    private String firstName, lastName;

    public String FirstName 
    {
        get { return firstName; }
        set { firstName = value; }
    }

    public String LastName 
    {
        get { return lastName; }
        set { lastName = value; }
    }
}

class Factory 
{
    public IPerson MakePerson() 
    {
        PersonImpl person = new PersonImpl();
        person.FirstName = 'Joe';
        person.LastName = 'Schmoe';
        return person;
    }
}

이 접근법의 유일한 단점은 구현 클래스로 간단히 캐스트 할 수 있다는 것입니다. 보안상의 문제라면이 방법을 사용하는 것만으로는 충분하지 않습니다. 이에 대한 해결 방법은 은 변경 가능 클래스를 랩핑하기 파사드 클래스를 입니다. 이는 호출자가 작업하고 내부 오브젝트에 액세스 할 수없는 인터페이스를 표시합니다.

이런 식으로 캐스팅조차도 도움이되지 않습니다. 둘 다 동일한 읽기 전용 인터페이스에서 파생 될 수 있지만 반환 된 객체를 캐스팅하면 Facade 클래스 만 제공됩니다.이 클래스는 래핑 된 가변 클래스의 기본 상태를 변경하지 않으므로 변경할 수 없습니다.

이것은 불변의 객체가 생성자를 통해 한 번만 구성되는 일반적인 추세를 따르지 않는다는 점을 언급 할 가치가 있습니다. 당연히 많은 매개 변수를 처리해야 할 수도 있지만 이러한 모든 매개 변수를 미리 정의해야하는지 또는 나중에 소개 할 수 있는지 스스로에게 문의해야합니다. 이 경우 필요한 매개 변수 만있는 간단한 생성자를 사용해야합니다. 다시 말해,이 패턴이 프로그램의 다른 문제를 덮고 있다면 사용하지 마십시오.


1
"읽기 전용"개체를 반환하면 보안이 더 나아지지 않습니다. 개체를 가져 오는 코드는 여전히 리플렉션을 사용하여 개체를 수정할 수 있기 때문입니다. 리플렉션을 사용하여 문자열을 수정할 수도 있습니다 (복사하지 않고 제자리에서 수정).
MTilsted

보안 문제는 개인 수업으로 깔끔하게 해결 될 수 있습니다
Esben Skov Pedersen

@Esben : 여전히 MS07-052 : 코드 실행 결과 코드 실행 결과 와 경쟁해야 합니다. 코드는 코드와 동일한 보안 컨텍스트에서 실행되므로 디버거를 연결하고 원하는대로 수행 할 수 있습니다.
Kevin

Kevin1 모든 캡슐화에 대해 말할 수 있습니다. 나는 반사로부터 보호하려고 노력하지 않는다.
Esben Skov Pedersen

1
"security"라는 단어를 사용할 때의 문제점은 누군가가 내가 더 안전한 옵션이라고 말한 것이 최대 보안 및 모범 사례와 같다고 가정한다는 것입니다. 나도 말한 적이 없어 누군가가 사용할 수 있도록 라이브러리를 제공하는 경우, 난독 처리되지 않은 경우 (때로는 난독 화 된 경우가 아니라면) 보안 보장을 잊어 버릴 수 있습니다. 그러나 리플렉션을 사용하여 반환 된 파사드 객체를 훼손하는 경우 포함 된 내부 객체를 검색하기 위해 라이브러리를 사용하지 않아야한다는 사실에 모두 동의 할 수 있습니다.
Neil

8

@JacquesB가 말한 것처럼 Builder 패턴을 사용하거나 실제로 왜 이러한 객체의 객체 작성 중 변경할 수 있나요?

다시 말해, 필요한 모든 값을 생성자에 전달하고 한 번에 인스턴스를 생성하는 것과 달리 왜 생성 프로세스가 시간에 따라 확산되어야 하는가?

빌더는 잘못된 문제점에 대한 좋은 솔루션 일 수 있기 때문입니다.

문제가 10 개의 매개 변수 길이와 같은 생성자로 끝나고 개체를 조금씩 작성하여 완화하려는 경우 디자인이 엉망이고이 10 개의 값은 " bagged "/ 몇 개의 객체로 그룹화되거나 ... 또는 주된 객체가 몇 개의 작은 객체로 분할되었습니다 ...

이 경우 불변성을 고수하고 디자인을 개선하십시오.


코드가 여러 데이터베이스 쿼리를 수행하여 데이터를 가져 오기 때문에 한 번에 작성하기가 어려울 수 있습니다. 다른 테이블과 다른 데이터베이스의 데이터를 비교합니다.
폴 리차드
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.