两个不同接口实现中的重复代码

Duplicate code inside two different Interface implementations

对于每个实体,我正在制作一个控制器、一个服务和一个 DAO。我现在有大约 8 个实体与那些 classes。举个例子,我的 classes CategorieProduct.

Class CategorieDaoImpl 实现了 CategorieDao

的方法
@Override
public boolean insertCategorie(Categorie categorie) {
    Session session = null;
    try {
        session = super.getConnection();
        session.getTransaction().begin();
        session.save(categorie);
        session.getTransaction().commit();
        return true;
    } catch (HibernateException e) {
        e.printStackTrace();
        return false;
    } finally {
        closeConnection(session);
    }
}

Class ProductDaoImpl 实现了 ProductDao

的方法
@Override
public boolean insertProduct(Product product) {
    Session session = null;
    try {
        session = super.getConnection();
        session.getTransaction().begin();
        session.save(product);
        session.getTransaction().commit();
        return true;
    } catch (HibernateException e) {
        e.printStackTrace();
        return false;
    }
    finally {
        closeConnection(session);
    }
}

如你所见,两个class的代码除了参数和save().

的参数外,还是很相似的

Intellij 告诉我这是重复代码,但是当重复代码位于 class 中时,它并没有像通常那样给我解决方案。知道如何解决这个问题并让它变得更好吗?

提前致谢。

编辑:

大多数 Dao classes 具有相同的 CRUS 方法:获取、插入、更新、删除。主要是参数不同而已。

最简单的解决方案是:

public boolean insertGeneric(Object whatever) {
    Session session = null;
    try {
        session = super.getConnection();
        session.getTransaction().begin();
        session.save(whatever);
        session.getTransaction().commit();
        return true;
    } catch (HibernateException e) {
        e.printStackTrace();
        return false;
    } finally {
        closeConnection(session);
    }
}

当然,更"generic answer"可能是你使用了一些<T extends some BaseType>类型参数而不是Object

如果没有这样的基础 class,下一个最好的办法是确定一个公共基础接口或您打算保存的所有对象 could/should/need-to 实现的东西。

换句话说:您的选择 space 很大程度上取决于 session.save()!

的实际签名

可以用一个 AbstractInsertable<T>-class 来解决这个问题,其中有一些 public boolean insert(T t) 持有您的代码:

public abstract class AbstractInsertable<T> extends ... {
    public boolean insert(T t) {
        Session session = null;
        try {
            session = super.getConnection();
            session.getTransaction().begin();
            session.save(t);
            session.getTransaction().commit();
            return true;
        } catch (HibernateException e) {
            e.printStackTrace();
            return false;
        } finally {
            closeConnection(session);
        }
    }

    [...]
}

然后可以从 AbstractInsertable<T> 继承实现,例如CategorieDaoImpl extends AbstractInsertable<Category>。当然,这只有在一个人只继承一个人的情况下才有效 class.

另一种选择是使用接口和默认实现。

答案到此结束。剩下的就是我个人的看法了。


我个人的愿望是 Java 允许针对这些确切问题的多重继承:可以为每个 CRUD 操作定义一个 class 并将它们用作 Dao-实现。接口中的默认方法非常接近多重继承,但有一些限制,例如所有方法都必须是 public 并且不能定义属性,多重继承不会出现这些属性。


对您的代码的一个小评论:您有可能发生 NullPointerException

        Session session = null;
        try {
            session = super.getConnection();
            [...]
        } finally {
            closeSession(session);
        }

在不知道 closeSession(...) 的确切实现的情况下,如果您没有执行 nullcheck,我不会感到惊讶,因此可能会抛出 NPE。如果 Session 是对您有利的 AutoCloseable, you could use a try-with-resources. If it is not, you could use Optional

        Optional<Session> optionalSession = Optional.empty();
        try {
            optionalSession = Optional.of(super.getConnection());
            session = optionalSession.get();
            [...]
        } finally {
            optionalSession.ifPresent(this::closeSession);
        }