Is extending a "base" class (often abstract) an anti-pattern?
Since you can't "hide" your class's supertypes from your class's clients, its entire type hierarchy is part of its API, including the "base" class. Clients can store your implementation in a variable of the abstract "base" type. Good luck changing your supertype after that.
Since the "base" class does not define semantics of the class and is only extended to get access to certain implementation decisions, it's an implementation detail.
Hence, it makes implementation details part of your class's API which is strictly forbidden in software design.
True, you can make the "base" class package-private, but I'm personally not a big fan of package-private classes since they restrict my ability to change the package hierarchy.
If it is an anti-pattern, should you make your class contain the base class ("prefer composition over inheritance")? Won't it be confusing?
Despite my criticism of the "base" classes, they are convenient, pretty common, especially in older APIs (e.g. Swing) and in practice cause little harm.
Here's an example. There's a BaseDao class in our codebase. It's not technically abstract but, to the best of my knowledge, it is not directly instantiated anywhere, so you can say it's effectively abstract. Specific DAOs extend from it, inheriting protected convenience methods, for example findWithAppSql().
Note. I mentioned appSql strings in one of my earlier questions. Basically, it's a named SQL statement. They are meant to somewhat decouple clients from the DB: the clients first fetch a statement string by its name, put parameter values if necessary, and then actually hit the DB with that statement. For example, there could be an appSql string called SELECT_DOCTORS which contains a query like SELECT * FROM doctor d WHERE (:ID IS NULL or d.id = :ID). You can say appSql strings are the DB's API, similar to how HTTP endpoints are the API of a web server. We don't have a separate backend layer. Instead, the DB in effect doubles as the backend.
public class BaseDao {
// ...
protected <T> List<T> findWithAppSql(String appSql, Class<T> entityType, QueryData params) {
public class DoctorDao extends BaseDao {
public List<DOCTOR> findDoctors() {
return findWithAppSql("SELECT_DOCTORS", DOCTOR.class, QueryData.empty());
}
If you make the BaseDao methods public and make it a field instead of supertype of "specific" DAOs, a few things happen.
- It becomes so tempting to inject the
BaseDaodirectly, especially if clients need to load multiple types of entities. Why do you need to inject, like, three or four DAOs if you can injectBaseDaoand load any entities you want? Surely, it would make an inferior design, but I can easily imagine that happening. Keep in mind, we don't use ORM, so if you need to fetch, say, a doctor's department that they reference with a foreign key, you can't just calldoctor.getDepartment()and expect it to return a referenced entity. Instead, you would calldoctor.getDepartmentId()and fetch the department yourself (probably, with aDepartmentDao). - It becomes semantically confusing. Why is it a field if it's called a base DAO? Giving it a new name can clear things up (for example,
ReinventedEntityManager).