The General Responsibility Assignment Software Pattern (GRASP) guides in assigning responsibilities to collaborating objects. The GRASP principles aim to make software design more maintainable, modular, and reusable by providing a better understanding of how to distribute responsibilities among different parts of the software system.

Responsibility Driven Design (RDD)

RDD is an approach that focuses on defining the responsibilities of classes and objects within a system. Responsibility is a contract or obligation of a class

Responsibilities are assigned to classes of objects during object design, RDD encourages a top-down approach, where designer starts by designing high-level responsibilities and then decompose them into smaller responsibilities, eventually assign them to specific classes.

A class must know its responsibility

  • Private encapsulated data Some data should be encapsulated and hidden from other classes, promoting data integrity and preventing unintended modifications.

  • Related objects A class should be aware of the other classes it collaborates with. This awareness allows objects to interact and fulfil their responsibilities.

  • Things it can derive or calculate A class should know what data it can derive or calculate based on its responsibilities.

A class must do its responsibility

  • Take action (create objects, do calculations) A class should be able to perform actions related to its responsibilities.

  • Initiate action in other objects Sometimes, a class might need to trigger actions in other classes to fulfil its responsibilities.

  • Control/coordinate actions in other objects Controller classes help manage the flow of information and actions between different parts of the system.

Responsibility Examples

”A Sale is responsible for knowing its total” (knowing) Knowing responsibilities are related to attributes, associations in the domain model

”A Sale is responsible for creating SalesLineItems” (doing) Doing responsibilities are implemented by means of methods.

Methodological approach to OO design

The five basic principles

  • Creator
  • Information Expert
  • High Cohesion
  • Low Coupling
  • Controller

Creator principle

Assign class B the responsibility to create an instance of a class A if one of these is true:

B “contains” A

A has a “part of” relationship between B, which can be aggregation or composition. This containment implies a strong relationship where B manages the life cycle of A.

public class Building {
    private List<Room> rooms;
 
    public Building() {
        this.rooms = new ArrayList<>();
    }
 
    public void addRoom(int area) {
        Room newRoom = new Room(area);
        rooms.add(newRoom);
    }
}
 
public class Room {
    private int area;
 
    public Room(int area) {
        this.area = area;
    }
}
 

B “records” A

If B is responsible for recording instances of class A (storing or keeping track of them), then class B should create instances of class A. e.g. A Library class that keeps track of Book records might be responsible for creating new Book instances when new book record is to be added.

public class Library {
    private List<Book> books;
 
    public Library() {
        this.books = new ArrayList<>();
    }
 
    public void addBook(String title, String author) {
        Book newBook = new Book(title, author);
        books.add(newBook);
    }
}
 
public class Book {
    private String title;
    private String author;
 
    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }
}

B “closely uses” A

When class B uses class A’s instances frequently, it is practical for B to create instances of A. This is often when functionality of A is essential to the operation of B.

public class AcrylicPainter {
    private Brush brush = new Brush("acrylic");
    
    public void paint() {
        brush.applyPaint();
    }
}
 
public class Brush {
    private String paintType;
 
    public Brush(String paintType) {
        this.paintType = paintType;
    }
 
    public void applyPaint() {
        // Painting logic
    }
}
 

B “has the initial data for” A

If class B has the data necessary to initialise objects of class A, then it is suitable for B to create instances of A.

public class Concert {
    private String bandName;
    private Date date;
 
    public Concert(String bandName, Date date) {
        this.bandName = bandName;
        this.date = date;
    }
 
    public Ticket sellTicket(String seat) {
        return new Ticket(this.bandName, this.date, seat);
    }
}
 
public class Ticket {
    private String bandName;
    private Date date;
    private String seat;
 
    public Ticket(String bandName, Date date, String seat) {
        this.bandName = bandName;
        this.date = date;
        this.seat = seat;
    }
}

Information expert principle

Assign responsibility to the classes that has the information needed to fulfil it. All methods that work with data (variables, files) should be in the same place where data exist.

public class Cart {
    private List<Item> items = new ArrayList<>();
 
    public void addItem(Item item) {
        items.add(item);
    }
 
    public double calculateTotalPrice() {
        double total = 0;
        for (Item item : items) {
            total += item.getPrice();
        }
        return total;
    }
}

In this shopping cart example, the Cart class should be responsible for calculating the total price since it holds all the Item objects.

Low coupling principle

Design classes with minimal dependencies on other classes. Low coupling reduces the impact of changes, making system more maintainable and scalable.

For more details with example, proceed to Low Coupling Principle.

High cohesion

Design classes that their responsibilities are strongly related and highly focused. High cohesion makes classes easier to maintain and understand.

public class UserManager {
    public boolean authenticateUser(String username, String password) {
        // Authentication logic
    }
 
    public void createUser(String username, String password) {
        // User creation logic
    }
}

In this example, a UserManager class should only handle user-related responsibilities such as user authentication, and not handle tasks like logging or database connections.

Controller

A controller assigns responsibilities of handling system events to a class. The controller can either:

public class LoginController {
    private UserManager userManager;
 
    public LoginController(UserManager userManager) {
        this.userManager = userManager;
    }
 
    public String login(String username, String password) {
        boolean isValidUser = userManager.authenticateUser(username, password);
        if (isValidUser) {
            return "Login Successful";
        } else {
            return "Login Failed";
        }
    }
}

The controller class represents the login use case of a software system. It acts as an intermediary in the context of a MVC system.


Back to parent page: Web and Application Development

Design_PrincipleSOFT2201GRASP_design_principlesResponsibility_driven_designRDD