Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Low-Level Design (Object-Oriented Design)  Module 4 – Design principles: SOLID and beyond

GRASP: assigning responsibilities

GRASP in practice: decide which class should do what with Information Expert, Creator, Controller and six more patterns, each one checked by running code.

  • Intermediate
  • 25 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7 and Node.js 24.21.0
  • By MySmartCoPilot
Free lesson, Pro answers: 25 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Apply Information Expert, Creator and Controller to decide where a method belongs
  • Use Low Coupling and High Cohesion to judge between two candidate owners
  • Use a Pure Fabrication or an Indirection when no domain class fits
  • Justify each assignment in an interview answer with one GRASP name and one reason

Before you start

What you’ll learn · Before you start

What you will learn

  • Apply Information Expert, Creator and Controller to decide where a method belongs
  • Use Low Coupling and High Cohesion to judge between two candidate owners
  • Use a Pure Fabrication or an Indirection when no domain class fits
  • Justify each assignment in an interview answer with one GRASP name and one reason

Before you start

On this page

Every low-level design is a long series of small decisions about the same question: which class should do this? GRASP gives the good answers names. Craig Larman collected them in his book Applying UML and Patterns as nine General Responsibility Assignment Software Patterns: Information Expert, Creator, Controller, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection and Protected Variations. Each one turns a placement into a reason you can say in a sentence, such as “the booking adds up its total because it holds the lines”.

Responsibilities, and nine ways to place them

A responsibility is something a class knows or does. Knowing covers its own data and what it can work out from it; doing covers acting, creating other objects and coordinating them. The previous lessons asked how to split classes. GRASP starts from a responsibility you already have, such as “compute the bill” or “start a rental”, and asks who should get it. Each pattern answers one form of that question:

  • Information Expert. Who should do this? The class that has the data the job needs.
  • Creator. Who should create an object? A class that contains it, records it, works closely with it or has the data to start it.
  • Controller. Who receives a request from the user interface? An object behind the interface that stands for the system or for one use case, and delegates.
  • Low Coupling. Which of two options is better? The one that adds fewer dependencies.
  • High Cohesion. Which of two options is better? The one that keeps each class focused on one job.
  • Polymorphism. Where does behaviour that varies by type go? Into the types, not into switch statements.
  • Pure Fabrication. What if no domain class fits? An invented class, such as a repository or a printer, that keeps the domain classes cohesive.
  • Indirection. How do two parts avoid depending on each other? An object between them, such as an adapter.
  • Protected Variations. How is a design protected from a change that is expected? A stable interface around the point that varies.

Two of the nine, Low Coupling and High Cohesion, are not answers but judges: they decide between the candidates that the others suggest. The first lesson of this module defined both.

Five decisions in a cinema booking model

A cinema sells seats for its shows. Recliners and regular seats have different prices, a customer books several seats at once, the card is charged, and a ticket is printed. Here is a small model whose comments name the pattern behind each placement, and a script that checks five of the decisions by running them:

Five responsibility decisions, each one checked

booking/decisions.py

"""Five "who should do this?" decisions in the cinema model, each checked by running the code."""

from cinema import Booking, BookingService, PaymentDeclined, Seat, SeatTaken, Show, TicketPrinter


class RecordingGateway:
    def __init__(self):
        self.charges = []

    def charge(self, amount, reference):
        self.charges.append((amount, reference))
        return f"pay-{len(self.charges)}"


class DecliningGateway:
    def charge(self, amount, reference):
        raise PaymentDeclined("card declined")


def new_show():
    seats = [Seat("A1", "recliner"), Seat("A2", "recliner"), Seat("B4", "regular")]
    return Show("show-7", seats, prices={"recliner": 220, "regular": 100})


def check(name, passed, detail):
    print(f"{'ok' if passed else 'FAILED':6} {name}: {detail}")


show = new_show()
booking = show.book(["A1", "A2", "B4"], "Asha")
check("Information Expert", booking.total() == 540, f"Booking.total() adds its own lines: {booking.total()}")
check("Creator", isinstance(booking, Booking) and not show.is_free("A1"), "Show.book() creates the Booking from its seats and prices")

gateway = RecordingGateway()
service = BookingService([new_show()], gateway, TicketPrinter())
ticket = service.book_seats("show-7", ["B4"], "Ravi")
check("Controller", gateway.charges == [(100, "show-7:Ravi")], "BookingService.book_seats() takes the event, delegates and charges once")
check("Pure Fabrication", ticket == "show-7 | Ravi | seats B4 | paid 100" and not hasattr(Booking, "render"), "TicketPrinter formats the ticket; Booking has no layout code")

show = new_show()
declining = BookingService([show], DecliningGateway(), TicketPrinter())
try:
    declining.book_seats("show-7", ["A1"], "Meera")
except PaymentDeclined:
    pass
check("Protected Variations", show.is_free("A1"), "any PaymentGateway fits; after a declined card the seat is free again")

try:
    new_show().book(["A1", "A1"], "Kabir")
    duplicate = "accepted"
except SeatTaken:
    duplicate = "refused"
print(f"booking the same seat twice in one request: {duplicate}")

Output

ok     Information Expert: Booking.total() adds its own lines: 540
ok     Creator: Show.book() creates the Booking from its seats and prices
ok     Controller: BookingService.book_seats() takes the event, delegates and charges once
ok     Pure Fabrication: TicketPrinter formats the ticket; Booking has no layout code
ok     Protected Variations: any PaymentGateway fits; after a declined card the seat is free again
booking the same seat twice in one request: refused

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 decisions.py

booking/cinema.py

"""A small cinema booking model. Each responsibility sits where a GRASP pattern puts it (comments name the pattern)."""

from dataclasses import dataclass
from typing import Protocol


class SeatTaken(Exception):
    pass


class PaymentDeclined(Exception):
    pass


@dataclass(frozen=True)
class Seat:
    seat_id: str
    category: str  # "recliner" or "regular"


@dataclass(frozen=True)
class BookedSeat:
    seat: Seat
    price: int


class Booking:
    def __init__(self, customer, show_id, lines):
        self.customer = customer
        self.show_id = show_id
        self.lines = list(lines)

    def total(self):
        # Information Expert: the booking holds its lines, so it adds them up.
        return sum(line.price for line in self.lines)

    def seat_ids(self):
        return [line.seat.seat_id for line in self.lines]


class Show:
    def __init__(self, show_id, seats, prices):
        self.show_id = show_id
        self._seats = {seat.seat_id: seat for seat in seats}
        self._prices = dict(prices)  # category -> price
        self._taken = set()

    def is_free(self, seat_id):
        # Information Expert: the show knows which of its seats are taken.
        return seat_id not in self._taken

    def book(self, seat_ids, customer):
        # Creator: the show has the data a booking starts from (its seats and prices), so it creates the Booking.
        if len(set(seat_ids)) != len(seat_ids):
            raise SeatTaken("the same seat appears twice in one request")
        taken = [seat_id for seat_id in seat_ids if not self.is_free(seat_id)]
        if taken:
            raise SeatTaken(", ".join(taken))
        self._taken.update(seat_ids)
        seats = [self._seats[seat_id] for seat_id in seat_ids]
        return Booking(customer, self.show_id, [BookedSeat(seat, self._prices[seat.category]) for seat in seats])

    def release(self, booking):
        self._taken.difference_update(booking.seat_ids())


class PaymentGateway(Protocol):
    # Protected Variations and Indirection: payment providers change, so the service depends on this interface.
    def charge(self, amount: int, reference: str) -> str: ...


class TicketPrinter:
    # Pure Fabrication: no cinema concept "prints"; this class exists so that Booking stays free of text layout.
    def render(self, booking):
        seats = ", ".join(booking.seat_ids())
        return f"{booking.show_id} | {booking.customer} | seats {seats} | paid {booking.total()}"


class BookingService:
    # Controller: the first object behind the user interface that receives the "book seats" system event. It
    # coordinates and decides nothing that another object knows better.
    def __init__(self, shows, gateway: PaymentGateway, printer: TicketPrinter):
        self._shows = {show.show_id: show for show in shows}
        self._gateway = gateway
        self._printer = printer

    def book_seats(self, show_id, seat_ids, customer):
        show = self._shows[show_id]
        booking = show.book(seat_ids, customer)
        try:
            self._gateway.charge(booking.total(), reference=f"{show_id}:{customer}")
        except PaymentDeclined:
            show.release(booking)
            raise
        return self._printer.render(booking)
  • Information Expert. Booking.total() adds the prices of its own lines. The show is the expert on which of its seats are free, so is_free() belongs to Show.
  • Creator. Show.book() creates the Booking, because the show has what a booking starts from: the seats and their prices. Any other creator would have to ask the show for them first.
  • Controller. BookingService.book_seats() receives the “book seats” request from whatever user interface exists, finds the show, asks it to book, charges and asks for a ticket. It works nothing out itself.
  • Pure Fabrication. No cinema concept prints tickets. TicketPrinter is invented so that Booking does not change every time the ticket layout does.
  • Protected Variations. Payment providers change, so the service depends on a PaymentGateway interface. A declined card releases the seats, and any gateway, real or fake, fits.

The last line of the output is an edge case that a first version of this model got wrong: a request for the same seat twice was accepted and charged twice. Where should the check go? The show is the expert on its seats, so the fix is two lines in Show.book(), not a check in the controller that every other caller of book() would miss.

The cinema model: BookingService as controller, Show creating the Booking, a TicketPrinter and a PaymentGateway interface.1. Booking seats2. Around the bookingScreen or command lineBookingServiceControllerShowCreator of Booking,expert on its seatsBookingexpert on its totalBookingServiceTicketPrinterPure FabricationPaymentGatewayProtected VariationsA card gateway, or afake one in testsbook seatsbook()createsrender()charge()implemented by

Who does what in the cinema model, and the GRASP pattern behind each choice

Text description of the diagram

The diagram shows the cinema booking model in two panels, one above the other. Each arrow points from the object that calls or creates to the one it calls or creates.

  1. Booking seats: the screen or command line sends the "book seats" event to BookingService, the Controller. BookingService calls book() on the Show. The Show is the Creator of the Booking, because it has the seats and prices a booking starts from, and the expert on which of its seats are free. The Booking is the expert on its own total.
  2. Around the booking: BookingService calls render() on TicketPrinter, a Pure Fabrication that keeps text layout out of the Booking, and charge() on PaymentGateway, an interface that protects the service from changes of payment provider (Protected Variations). A card gateway implements that interface in production, and a fake one in tests.

Moving work to the expert

A common first design gives the controller the work, because the controller is where the request arrives. In this bookshop checkout, the controller adds up the order by reading every line’s price and quantity, and applies the rule “10 % off a line of three or more copies”. The second version moves the line’s arithmetic into Line.subtotal() and the sum into Order.total(). The same checks run against both, and the script reports where each version keeps the discount rule:

The same checks before and after moving the total to the expert

expert/same_checks.py

"""The same checks against both versions, and where each version keeps the discount rule."""

import ast
from pathlib import Path

import after
import before


def run_checks(module):
    controller = module.CheckoutController()
    order = module.Order([module.Line("Godan", 300, 1), module.Line("Malgudi Days", 250, 4)])
    results = [
        controller.total(module.Order([])) == 0,
        controller.total(module.Order([module.Line("Godan", 300, 2)])) == 600,
        controller.total(order) == 300 + 900,  # 4 x 250 = 1000, less 10 %
        controller.summary(order) == "2 titles, total 1200",
    ]
    return sum(results), len(results)


def where_the_rule_lives(module):
    """The class and method that compare a quantity with 3: the discount rule."""
    tree = ast.parse(Path(module.__file__).read_text(encoding="utf-8"))
    for cls in (node for node in tree.body if isinstance(node, ast.ClassDef)):
        for method in (node for node in cls.body if isinstance(node, ast.FunctionDef)):
            for node in ast.walk(method):
                if isinstance(node, ast.Compare) and any(getattr(c, "value", None) == 3 for c in node.comparators):
                    return f"{cls.name}.{method.name}()"


for module in (before, after):
    passed, total = run_checks(module)
    print(f"{module.__name__}.py: {passed} of {total} checks pass; the discount rule is in {where_the_rule_lives(module)}")

Output

before.py: 4 of 4 checks pass; the discount rule is in CheckoutController.total()
after.py: 4 of 4 checks pass; the discount rule is in Line.subtotal()

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 same_checks.py

expert/before.py

"""A bookshop's checkout, first version: the controller computes the total from the order's data."""


class Line:
    def __init__(self, title, price, quantity):
        self.title = title
        self.price = price
        self.quantity = quantity


class Order:
    def __init__(self, lines):
        self.lines = list(lines)


class CheckoutController:
    def total(self, order):
        total = 0
        for line in order.lines:
            amount = line.price * line.quantity
            if line.quantity >= 3:
                amount -= amount // 10  # 10 % off a line of three or more copies
            total += amount
        return total

    def summary(self, order):
        return f"{len(order.lines)} titles, total {self.total(order)}"

expert/after.py

"""The same checkout after moving each calculation to the class that has the data (Information Expert)."""


class Line:
    def __init__(self, title, price, quantity):
        self.title = title
        self.price = price
        self.quantity = quantity

    def subtotal(self):
        amount = self.price * self.quantity
        if self.quantity >= 3:
            amount -= amount // 10  # 10 % off a line of three or more copies
        return amount


class Order:
    def __init__(self, lines):
        self.lines = list(lines)

    def total(self):
        return sum(line.subtotal() for line in self.lines)


class CheckoutController:
    def total(self, order):
        return order.total()

    def summary(self, order):
        return f"{len(order.lines)} titles, total {order.total()}"

Nothing a caller sees has changed, and the rule now lives with the data it reads. A second caller that needs an order’s total, such as an invoice, calls order.total() instead of copying the loop, and a change to the discount touches one method.

When the expert is the wrong answer

Information Expert, followed alone, would put strange things in strange places. An Order has all the data needed to save itself to a database, and a Booking all the data a ticket shows. If they did those jobs, Order would change whenever the database does and Booking whenever the ticket layout does, and both would depend on code that has nothing to do with orders or bookings. That is where the judges step in: High Cohesion and Low Coupling overrule the expert, and a Pure Fabrication takes the job. A repository saves orders; a printer prints tickets. They are invented classes, and they are legitimate because they keep the domain classes focused.

Common mistake

A controller that works everything out itself grows into the class that every change touches: it computes fees, picks seats, formats tickets and talks to the payment provider. Keep controllers thin. Each line of a controller method should be a call to an object that knows more about that step than the controller does.

Controllers, polymorphism and protected variations

Larman describes two kinds of controller. A facade controller stands for the whole system or a large part of it, such as BookingService. A use-case controller stands for one scenario, such as RefundHandler, and suits systems with many use cases. In a machine-coding round the service layer called by your command parser plays this role.

The other three patterns appeared earlier in this module under other names. Polymorphism is the vehicle types of the Open/Closed lesson: behaviour that varies by type goes into the types. Indirection is any object placed between two others so that they do not depend on each other, as an adapter between the booking service and a payment provider does. Protected Variations asks you to find the points where change is expected, such as the payment provider or the pricing rules, and to put a stable interface around each. Larman’s column on it in IEEE Software is called “Protected Variation: The Importance of Being Closed”, a nod to the Open/Closed Principle, and the idea goes back to Parnas’s advice to hide each design decision that is likely to change.

In a design interview

Say the reason as you place each method: “the show creates the booking because it has the seats and the prices”; “the service is the controller, so it only coordinates”; “the printer is a fabrication, so the booking has no layout code”. One pattern name and one reason per decision is enough, and it answers the follow-up “why did you put that there?” before it is asked.

Python Online Compiler Add a convenience fee per booking to the cinema model and decide, with a GRASP name, which class should own it.

Interview questions

Warm-up (fresher to mid level): what is GRASP, and what does Information Expert say? GRASP is Craig Larman’s set of nine patterns for deciding which class gets a responsibility: Information Expert, Creator, Controller, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection and Protected Variations. Information Expert says to give a responsibility to the class that has the information needed to carry it out, so a booking computes its own total from its lines instead of a controller reading every line. It is usually the first pattern to try, because it keeps behaviour next to its data and needs no extra dependencies. It is overruled when following it would hurt cohesion or coupling: an order has the data to save itself, but saving belongs to a repository, a Pure Fabrication, so the order does not depend on the database.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    In a machine-coding round your ParkingService has grown to 600 lines: it computes fees, picks free spots, prints tickets and calls the payment API. Using GRASP, how would you redistribute these responsibilities, and how would you justify each move in one sentence?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A candidate argues that Order should have save() and to_pdf() methods, because by Information Expert it has all the data they need. Do you agree? Answer with GRASP, and say where those two responsibilities should go.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • GRASP names nine ways to decide which class gets a responsibility; Low Coupling and High Cohesion judge between the options the others suggest.
  • Information Expert: give the job to the class with the data. Creator: let the class that contains, records or initialises an object create it.
  • Controller: one object behind the user interface receives each request and delegates; it decides nothing that another object knows better.
  • Pure Fabrication and Indirection are legitimate invented classes when a domain class would lose cohesion or gain a dependency.
  • Protected Variations puts a stable interface around each expected change, the idea behind many design patterns.

Exercise

Exercise · Medium · Python

Place two car-rental responsibilities with GRASP

A car-rental company's model has a Car, a Rental and a RentalDesk. Two responsibilities are still missing, and the classes they belong to are already chosen:

  1. Working out what a rental costs, in Rental.amount_due(km_driven). The rental knows its days and its car, and the car knows its rates, so the rental is the Information Expert. A rental costs the car's daily_rate for each day, plus extra_km_rate for every kilometre driven above the allowance, which is km_per_day for each day.
  2. Starting a rental, split between two classes. Car.rent_to(customer, days) creates the Rental (Creator: the car has the data a rental starts from): it raises ValueError when days is below 1 and CarUnavailable when the car is already rented, and otherwise marks the car as rented and returns the new rental. RentalDesk.start_rental(customer, plate, days) receives the request from the counter (Controller): it finds the car by its plate, asks it to rent itself, records the rental in rentals and returns it.

The sample tests check three bills, a recorded rental, a refused second rental of the same car and a rental of 0 days.

Starter code · car_rental.py

"""A car-rental model. Write the three methods that the exercise prompt describes; the rest is done."""


class CarUnavailable(Exception):
    pass


class Rental:
    def __init__(self, customer, car, days):
        self.customer = customer
        self.car = car
        self.days = days

    def amount_due(self, km_driven):
        """Write this: the car's daily rate for each day, plus its extra-kilometre rate for every kilometre driven
        above the allowance of km_per_day for each day."""
        return 0


class Car:
    def __init__(self, plate, daily_rate, km_per_day, extra_km_rate):
        self.plate = plate
        self.daily_rate = daily_rate
        self.km_per_day = km_per_day
        self.extra_km_rate = extra_km_rate
        self.rented = False

    def rent_to(self, customer, days):
        """Write this: refuse with ValueError when days is below 1, and with CarUnavailable when the car is already
        rented; otherwise mark the car as rented and return a new Rental."""
        return None


class RentalDesk:
    def __init__(self, cars):
        self._cars = {car.plate: car for car in cars}
        self.rentals = []

    def start_rental(self, customer, plate, days):
        """Write this: find the car by its plate, ask it to rent itself, record the rental and return it."""
        return None
The sample tests · test_car_rental.py
from car_rental import Car, CarUnavailable, Rental, RentalDesk


def hatchback():
    return Car("KA-01-AB-1234", daily_rate=1800, km_per_day=250, extra_km_rate=12)


def test_amount_within_the_allowance():
    """3 days and 600 km stay inside the 750 km allowance: 3 x 1800"""
    assert Rental("Asha", hatchback(), days=3).amount_due(km_driven=600) == 5400


def test_amount_at_the_allowance():
    """exactly 750 km in 3 days costs nothing extra"""
    assert Rental("Asha", hatchback(), days=3).amount_due(km_driven=750) == 5400


def test_amount_with_extra_kilometres():
    """900 km in 3 days is 150 km over the allowance, at 12 a kilometre"""
    assert Rental("Asha", hatchback(), days=3).amount_due(km_driven=900) == 5400 + 150 * 12


def test_the_desk_starts_and_records_a_rental():
    """the desk finds the car, the car creates the rental, the desk records it"""
    car = hatchback()
    desk = RentalDesk([car])
    rental = desk.start_rental("Ravi", "KA-01-AB-1234", days=2)
    assert isinstance(rental, Rental)
    assert (rental.customer, rental.car, rental.days) == ("Ravi", car, 2)
    assert car.rented is True
    assert desk.rentals == [rental]


def test_a_rented_car_is_refused():
    """a second rental of the same car is refused and nothing is recorded"""
    desk = RentalDesk([hatchback()])
    desk.start_rental("Ravi", "KA-01-AB-1234", days=2)
    try:
        desk.start_rental("Meera", "KA-01-AB-1234", days=1)
    except CarUnavailable:
        pass
    else:
        raise AssertionError("a car that is already rented must be refused")
    assert len(desk.rentals) == 1


def test_days_must_be_at_least_one():
    """a rental of 0 days is refused"""
    try:
        hatchback().rent_to("Kabir", days=0)
    except ValueError:
        pass
    else:
        raise AssertionError("days below 1 must raise ValueError")
A hint

For the bill, compute the allowance first (km_per_day times the days), then the extra kilometres, which are never negative: max(0, km_driven - allowance). For a new rental, keep the checks in Car.rent_to(), because the car is the one that knows whether it is free; the desk only finds the car, calls it and records what it returns.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 In the car-rental model, which class should compute the amount a customer owes when they return a car?

    Choose one answer.

    Show the answer to question 1

    Answer: Rental, because it has the days, the car and through it the rates (Information Expert)

    Information Expert gives a responsibility to the class with the information needed to carry it out. The rental already holds what the bill depends on, so the calculation needs no other object's internals. The desk receives the "return car" event and asks the rental; it should not do the arithmetic itself.

  2. Question 2 of 6 Which object should receive the "return car" request that the counter's screen sends?

    Choose one answer.

    Show the answer to question 2

    Answer: RentalDesk, the controller for the counter's requests

    GRASP's Controller is the first object behind the user interface that receives a system event and coordinates the work. Putting the logic in the screen ties the rules to one interface; sending the event straight to Car makes the car coordinate payments and records that are not its business.

  3. Question 3 of 6 The rental agreement must be produced as a printable document. Where should the layout code go?

    Choose one answer.

    Show the answer to question 3

    Answer: In a new AgreementPrinter class, a Pure Fabrication that reads the rental

    Information Expert alone would put layout in Rental, which would then change for pricing rules and for page design. A Pure Fabrication, a class that is not a domain concept, keeps Rental cohesive and gives the layout one home that can change on its own.

  4. Question 4 of 6 Hatchbacks, SUVs and electric cars are billed by different rules. How does GRASP suggest handling that?

    Choose one answer.

    Show the answer to question 4

    Answer: Polymorphism, with the billing rule as a method of each car category, so no code switches on the category

    When behaviour varies by type, Polymorphism gives the varying behaviour to the types. Adding a category then means adding a class, not finding every if statement, which is the Open/Closed Principle seen from GRASP.

  5. Question 5 of 6 The company may change its payment provider next year. What protects the rest of the design?

    Choose one answer.

    Show the answer to question 5

    Answer: Protected Variations, through an interface that the desk owns and an adapter for each provider

    Protected Variations identifies a point of likely change and puts a stable interface around it; Indirection is the adapter that sits between the desk and the provider. Changing providers then changes one adapter.

  6. Question 6 of 6 Two classes could both take a responsibility. Which GRASP principles do you use to judge between them?

    Choose every answer that is right.

    Show the answer to question 6

    Answer:

    • High Cohesion
    • Low Coupling

    Low Coupling and High Cohesion are the evaluative principles: prefer the option that adds fewer dependencies and keeps each class focused. Creator and Pure Fabrication are specific answers to specific questions, which the two judges help you check.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.