То же стирание для подкласса, который отображает родительский метод в другой класс - PullRequest
2 голосов
/ 18 мая 2019

Я пытаюсь реорганизовать DAO, чтобы сделать его немного более удобным в нашей базе кода.В настоящее время у нас есть параметризованный AbstractDao, который принимает три типа:

  1. Таблица базы данных
  2. База данных pojo
  3. Другое сопоставленное представление pojo 2)

Таким образом, в конечном итоге это выглядит так:

public class AbstractDao<T extends DatabaseTable, R extends DatabaseRecord, M> {
  public AbstractDao(Connection connection, Mapper<R,M> mapper) {
  //save connection and mapper to protected variables
}
public List<M> insert(List<M> records) {
 connection.insertBulk(
   StreamEx.of(records).map(mapper::map).toList()
 );
 }
}

Однако это не работает в классическом случае DAO, когда мы имеем дело только с pojo и столом.

Однако здесь есть общая функциональность, которую можно абстрагировать в более базовый AbstractDao, который полезен для всех проектов.Что-то вроде:

AbstractDao<T extends DatabaseTable, R extends Record>

, у которого есть подкласс

AbstractMappedDao<T extends DatabaseTable, R extends Record, M> extends AbstractDao<T, R>

Аннотация имеет метод, подобный:

public List<R> insert(List<R> records) {
  connection.insertBulk(records);
}

, и Mapped должен иметь метод, подобный:

public List<M> insert(List<M> records) {
  super.insert(StreamEx.of(records).map(mapper::map).toList());
}

Тем не менее, это приводит к проблеме "того же стирания", потому что вставка получает в списке универсальных элементов.

Я попытался абстрагировать его в интерфейс:

public interface Dao<T> {
  public List<T> insert(List<T> records);
}

И заставить Abstract реализовать Dao, а Mapped - Dao, но, опять же, та же проблема.

Итак, мой вопрос, как лучше всего подойти к этой проблеме?Это работает, как и ожидалось, если я изменю подпись карты на что-то вроде:

insertMapped(List<M> mapped);

Но я бы предпочел оставить контракт таким же.

Спасибо за помощь.С нетерпением жду обсуждения!

1 Ответ

1 голос
/ 18 мая 2019

Когда дело доходит до поведения составления всегда лучше использовать композицию вместо наследования, и это на самом деле ваш случай.mapper не увеличивает поведение, уже существующее в вашем Dao, так же как добавляет поведение, дополнительный уровень косвенности в нем;который не обязательно относится к Dao, как аспект / сквозная проблема .

Итак, я рекомендую создать один AbstractDaoкласс с возможностью компоновки mappers (вы можете иметь только один, как вам хотелось; но с композицией легко позволить одному объекту Dao поддерживать несколько картографов):

        private Map<Class, Function> mappers;

        public <M> void registerMapper(Class<M> mappingClass, Function<M, R> mapper) {
            mappers.put(mappingClass, mapper);
        }

Затем создайтеinsert метод, который позволяет обрабатывать предварительное преобразование записей, которые не расширяются Record с использованием зарегистрированного mappers, например:

        public <M> List<M> insert(List<M> records) {
            if (records.isEmpty()) return records;
            M rec = records.get(0);

            List<? extends Record> actualRecords = (rec instanceof Record) ? 
                    (List<Record>)records : createMappedRecords(records, rec.getClass());

            connection.insertBulk(actualRecords);
            return records;
        }

Это чище, более надежно и расширяемо, поскольку ваш insert может быть централизованно создан для решения всевозможных проблем с помощью составных задач .Полный код компиляции будет выглядеть примерно так:

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class ParentErasure {

    public abstract class AbstractDao<T extends DatabaseTable, R extends Record> {
        private Connection connection;
        private Map<Class, Function> mappers = new HashMap<>();

        public <M> void registerMapper(Class<M> mappingClass, Function<M, R> mapper) {
            mappers.put(mappingClass, mapper);
        }

        public <M> List<M> insert(List<M> records) {
            if (records.isEmpty()) return records;
            M rec = records.get(0);

            List<? extends Record> actualRecords = (rec instanceof Record) ? 
                    (List<Record>)records : createMappedRecords(records, rec.getClass());

            connection.insertBulk(actualRecords);
            return records;
        }

        private <M> List<R> createMappedRecords(List<M> records, Class<? extends Object> recordsClazz) {
            Function<M, R> mapper = mappers.get(recordsClazz);
            return records.stream()
                    .map(mapper::apply)
                    .collect(Collectors.toList());
        }
    }

    public interface Dao<T> {
        public List<T> insert(List<T> records);
    }
}

class Record {}
class DatabaseTable {}
class DatabaseRecord {}
class Connection {
    public void insertBulk(List<? extends Record> records) {}
}

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...