Клас із членами, які змінюються під час створення, але незмінними після цього


22

У мене є алгоритм, який створює колекцію об'єктів. Ці об'єкти підлягають зміні під час створення, оскільки вони починаються дуже мало, але потім вони заповнюються даними в різних місцях алгоритму.

Після завершення алгоритму об'єкти ніколи не повинні змінюватися - проте вони споживаються іншими частинами програмного забезпечення.

Чи вважається в цих сценаріях хорошою практикою наявність двох версій класу, як описано нижче?

  • Тоді змінюється створюється алгоритмом
  • Після завершення роботи алгоритму дані копіюються в незмінні об'єкти, які повертаються.

3
Чи можете ви відредагувати своє запитання, щоб уточнити, у чому полягає ваша проблема / питання?
Саймон Берго

Можливо, вас також зацікавить це питання: Як заморозити ескіз у .NET (зробити клас незмінним)
R0MANARMY

Відповіді:


46

Можливо, ви можете використовувати шаблон для побудови . Він використовує окремий об’єкт "builder" з метою збору необхідних даних, і коли всі дані збираються, він створює власне об'єкт. Створений об’єкт може бути незмінним.


Ваше рішення було правильним для моїх вимог (не всі вони були заявлені). Після дослідження повернені об'єкти не всі мають однакові характеристики. Клас будівельника має багато нульових полів, і коли дані накопичені, він вибирає, який з трьох різних класів генерувати: вони пов'язані один з одним за спадщиною.
Пол Річардс

2
@Paul У такому випадку, якщо ця відповідь вирішила вашу проблему, слід позначити її як прийняту.
Рікінг

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: Вам все одно доводиться боротися з MS07-052: Виконання коду призводить до виконання коду . Ваш код працює в тому ж контексті безпеки, що і їх код, тому вони можуть просто приєднати налагоджувач і робити все, що завгодно.
Кевін

Кевін1, можна сказати про всі інкапсуляції. Я не намагаюся захистити від роздумів.
Есбен Сков Педерсен

1
Проблема використання слова "безпека" полягає в тому, що одразу хтось припускає, що те, що я кажу як більш безпечний варіант, еквівалентне максимальній безпеці та кращій практиці. Я ніколи не сказав. Якщо ви передаєте бібліотеку комусь у користування, якщо тільки не затуманено (а іноді навіть при затуманеному), ви можете забути про гарантію безпеки. Однак я думаю, що всі ми можемо погодитися з тим, що якщо ви підробляєте повернений фасадний об'єкт, використовуючи відображення, щоб отримати внутрішній об'єкт, що міститься в ньому, ви не точно використовуєте бібліотеку, як це слід використовувати.
Ніл

8

Ви можете використовувати шаблон Builder так, як говорить @JacquesB, або подумати, чому ж насправді ці об'єкти повинні бути зміненими під час створення?

Іншими словами, чому процес їх створення повинен поширюватися в часі, на відміну від передачі всіх необхідних значень конструктору та створення екземплярів за один раз?

Тому що Builder може бути хорошим рішенням для неправильної проблеми.

Якщо проблема полягає в тому, що ви закінчите з конструктором, який має приблизно 10 параметрів, і ви хочете пом'якшити його, будуючи об'єкт потроху, це може означати, що дизайн зіпсований, і ці 10 значень повинні бути " мішок "/ згрупований у кілька об'єктів ... або основний об'єкт розділений на кілька менших ...

У такому випадку - дотримуйтесь незмінності до кінця, просто вдосконалюйте дизайн.


Створити це все буде важко відразу, оскільки код виконує кілька запитів до бази даних, щоб отримати дані; він порівнює дані в різних таблицях і в різних базах даних.
Пол Річардс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.