Java > Object-Oriented Programming (OOP) > Abstraction > Abstract Classes

Abstract Class Example: Data Access Object (DAO)

This example demonstrates abstraction in the context of a Data Access Object (DAO) pattern. We define an abstract `AbstractDAO` class that provides a common interface for accessing data. Concrete DAO classes (e.g., `UserDAO`, `ProductDAO`) extend the abstract class and provide implementations for specific data entities. This simplifies data access logic and promotes code maintainability.

Code Snippet: Abstract DAO and Concrete Implementations

The `AbstractDAO` class is declared as `abstract` and uses generics (``) to represent the type of data entity it manages. It defines abstract methods `getById()`, `save()`, and `delete()`, which must be implemented by concrete DAO classes. The `UserDAO` class extends `AbstractDAO` and provides concrete implementations for these methods, simulating data access operations. The `logAction()` method is a concrete method that provides a common logging functionality for all DAO classes.

// Abstract DAO class
abstract class AbstractDAO<T> {

    // Abstract method to get an entity by ID
    public abstract T getById(int id);

    // Abstract method to save an entity
    public abstract void save(T entity);

    // Abstract method to delete an entity
    public abstract void delete(T entity);

    // Concrete method for common operations (e.g., logging)
    protected void logAction(String action) {
        System.out.println("DAO Action: " + action);
    }
}

// User class (example data entity)
class User {
    private int id;
    private String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

// UserDAO class extending AbstractDAO
class UserDAO extends AbstractDAO<User> {

    @Override
    public User getById(int id) {
        // Simulate fetching user from a database
        logAction("Fetching user with ID: " + id);
        return new User(id, "User " + id);
    }

    @Override
    public void save(User user) {
        // Simulate saving user to a database
        logAction("Saving user: " + user.getName());
        System.out.println("User saved: " + user.getName());
    }

    @Override
    public void delete(User user) {
        // Simulate deleting user from a database
        logAction("Deleting user: " + user.getName());
        System.out.println("User deleted: " + user.getName());
    }
}

// Example usage
public class Main {
    public static void main(String[] args) {
        UserDAO userDao = new UserDAO();
        User user = userDao.getById(123);
        System.out.println("User ID: " + user.getId() + ", Name: " + user.getName());
        userDao.save(user);
        userDao.delete(user);
    }
}

Concepts Behind the Snippet

  • Abstraction: Abstract classes hide the specific data access implementations behind a common interface.
  • Generics: Generics allow the `AbstractDAO` to work with different types of data entities without needing to be rewritten for each type.
  • DAO Pattern: The DAO pattern separates data access logic from business logic, improving code maintainability and testability.

Real-Life Use Case

In a web application that interacts with a database, you would have different DAOs for different entities like users, products, orders, etc. An abstract DAO class could define common methods like `getConnection()`, `closeConnection()`, and error handling logic. Each concrete DAO would then implement methods like `getUserById()`, `saveProduct()`, etc., using the shared connection and error handling logic.

Best Practices

  • Use generics to make your abstract DAO class reusable for different data entities.
  • Define common data access operations in the abstract class.
  • Handle database connections and error handling in the abstract class.
  • Use dependency injection to provide the concrete DAO implementations to the business logic.

Interview Tip

Understand the benefits of using the DAO pattern, such as separation of concerns, improved testability, and reduced code duplication. Be able to explain how abstract classes can be used to implement the DAO pattern.

When to Use Them

Use abstract DAOs when you have multiple data entities that require similar data access operations. This promotes code reuse and simplifies data access logic. They are most useful in applications where you interact with databases or other data sources.

Memory Footprint

Similar to the previous example, the abstract DAO class itself has minimal memory footprint. The concrete implementations (e.g. `UserDAO`) will have memory footprint related to the state they manage, any cached data, or database connection objects. Optimizing database connections will generally have the largest effect on reducing memory usage.

Alternatives

  • Interfaces with default methods (Java 8+): You can use interfaces with default methods to provide some implementation details, but abstract classes generally offer more flexibility.
  • ORM Frameworks (e.g., Hibernate, JPA): These frameworks automate data access and persistence, reducing the need for manual DAO implementations.

Pros

  • Simplifies data access logic.
  • Promotes code reuse and maintainability.
  • Enforces a consistent data access interface.

Cons

  • Can add complexity if not designed carefully.
  • May not be necessary for simple applications.
  • Abstract DAO classes alone do not handle the underlying infrastructure like connection pooling.

FAQ

  • What is the purpose of using generics in the AbstractDAO class?

    Generics allow the `AbstractDAO` to work with different types of data entities without needing to be rewritten for each type. This promotes code reuse and type safety.
  • Can I have multiple concrete DAO classes extending the same AbstractDAO?

    Yes, you can have multiple concrete DAO classes extending the same `AbstractDAO`. Each concrete DAO would handle a different data entity (e.g., `UserDAO`, `ProductDAO`, `OrderDAO`).
  • Is it necessary to have an abstract method in an abstract class?

    No, it's not strictly necessary. An abstract class can exist without having any abstract methods. However, the main purpose of using an abstract class is often to define a common interface with some abstract methods that subclasses must implement.