Как можно добавить одно и то же поле в несколько классов, не используя наследование и не добавляя это поле в каждый класс индивидуально?

Как лучше всего добавить новое boolean поле к двум сущностям, соблюдая существующие код-конвенции?
  • 26 сентября 2024 г. 11:23
Ответы на вопрос 3
Для того чтобы добавить одно и то же поле в несколько классов без использования наследования и без необходимости добавлять его в каждый класс индивидуально, вы можете использовать композицию или интерфейсы. Вот несколько подходов:

1. **Создание отдельного класса для общего поля**:
   Создайте отдельный класс, который будет содержать ваше поле, и используйте его как составную часть в других классах.

   ```java
   public class CommonField {
       private boolean someFlag;

       public boolean isSomeFlag() {
           return someFlag;
       }

       public void setSomeFlag(boolean someFlag) {
           this.someFlag = someFlag;
       }
   }

   public class EntityA {
       private CommonField commonField = new CommonField();

       public CommonField getCommonField() {
           return commonField;
       }
   }

   public class EntityB {
       private CommonField commonField = new CommonField();

       public CommonField getCommonField() {
           return commonField;
       }
   }
   ```

2. **Использование интерфейсов (иногда с дефолтными методами)**:
   Вы можете создать интерфейс, который будет содержать метод для работы с общим полем, и реализовать его в нужных классах.

   ```java
   public interface CommonFieldAware {
       boolean isSomeFlag();
       void setSomeFlag(boolean someFlag);
   }

   public class EntityA implements CommonFieldAware {
       private boolean someFlag;

       @Override
       public boolean isSomeFlag() {
           return someFlag;
       }

       @Override
       public void setSomeFlag(boolean someFlag) {
           this.someFlag = someFlag;
       }
   }

   public class EntityB implements CommonFieldAware {
       private boolean someFlag;

       @Override
       public boolean isSomeFlag() {
           return someFlag;
       }

       @Override
       public void setSomeFlag(boolean someFlag) {
           this.someFlag = someFlag;
       }
   }
   ```

3. **Использование аннотаций (например, в JPA)**:
   Если вы работаете с ORM, вы можете использовать аннотации для общих свойств. Например, вы можете создать класс с аннотированным полем и использовать его через `@Embedded`.

   ```java
   @Embeddable
   public class CommonField {
       private boolean someFlag;

       // геттеры и сеттеры
   }

   @Entity
   public class EntityA {
       @Embedded
       private CommonField commonField;
   }

   @Entity
   public class EntityB {
       @Embedded
       private CommonField commonField;
   }
   ```

Выбор подхода зависит от вашей конкретной ситуации, предпочтений в проектировании и архитектуры приложения.
Привыкли к миксинам в языке с динамической типизацией? В C# их нет, и вряд ли будут, потому что типизация тут статическая. И приемы программирования - соответственно, другие. 

В частности, чтобы добавить дополнительное свойство (или метод) в классы из разных иерархий наследования, используйте интерфейс. Примерно так (примеры - на основе кода из комментариев):
public interface IChosable {
    bool IsChosen {get; set;}  //Желательно имена писать таки на английском правильном
                                             // а не ломаном. А то глаза режет.
                                             //Ну, или хотя бы - по русски транслитом: 
                                             // Vybrano вместо IsChoised - и то лучше, IMHO
}

//...
    public class ViewedDirectoryData : DirectoryDataEntity, IChosable
    {
        public bool IsChosen {get; set;}  
    }

    public class ViewedOutputData : OutputData, IChosable
    {
        public bool IsChosen {get; set;}  
    }

Но свойства, таки да, придется определять в каждом классе, реализующем интерфейс (но можно перенести их, вместе с указанием реализации интерфейса, в базовые классы иерархий).

Удобство тут в том, что ссылку на любой из классов, реализующих интерфейс, можно привести к типу интерфейса - тогда и статический контроль типов сохраните, и работать со всем этими классами можно будет единообразно.
Начиная с C#8 есть реализация интерфейсов по умолчанию. В туториалах майков есть пример реализации mixin'ов с их помощью - https://learn.microsoft.com/en-us/dotnet/csharp/ad...
Похожие вопросы