Intermediate 25 min read

Review and Refactor

By the end of this lesson, you'll be able to:

  • Review the complete feature across trigger, handler, and service class
  • Identify any remaining duplication or unclear naming
  • Confirm the final structure matches Module 24's trigger framework and Module 14's clean-code habits

Prerequisites: "Test the Feature"

The finished feature, end to end

trigger OpportunityTrigger on Opportunity (before update, after update) {
    OpportunityTriggerHandler handler = new OpportunityTriggerHandler();
    if (Trigger.isBefore) {
        handler.beforeUpdate(Trigger.new, Trigger.oldMap);
    }
    if (Trigger.isAfter) {
        handler.afterUpdate(Trigger.new, Trigger.oldMap);
    }
}

Three pieces, each with one clear job: OpportunityTrigger (routes events), OpportunityTriggerHandler (detects what changed and by how much), AccountRollupService (applies the adjustment). This mirrors every project module's closing lesson — a system that works, then reviewed once more with fresh eyes.

Checking against this course's standards

  • Bulk-safe? Yes — exactly one query and one DML statement in the rollup path, regardless of batch size (Module 25).
  • Thin trigger? Yes — the trigger body only routes to handler methods (Module 24).
  • Single Responsibility? Yes — three classes, three distinct jobs (Module 14).
  • Validated? Yes — a Closed Won Opportunity can't save without a positive Amount (this module, Lesson 4).
  • Verified? Yes, manually — formal automated tests are still ahead, in Module 31.

One more refactor: a shared constant

public class OpportunityTriggerHandler {
    private static final String CLOSED_WON = 'Closed Won';

    public void afterUpdate(List<Opportunity> newOpportunities, Map<Id, Opportunity> oldMap) {
        for (Opportunity opp : newOpportunities) {
            Opportunity oldOpp = oldMap.get(opp.Id);
            Boolean justWon = opp.StageName == CLOSED_WON && oldOpp.StageName != CLOSED_WON;
            // ...
        }
    }
}

The literal string 'Closed Won' appears in three separate places across this module's code so far — a classic candidate for Module 13's final keyword: one named constant, changed in exactly one place if the stage name ever needs to change, instead of hunting down every copy.

Exercise

Refactor this method to use a private static final String constant named MINIMUM_AMOUNT_ERROR instead of the repeated literal message.

Show hint

private static final String MINIMUM_AMOUNT_ERROR = '...';

APEX

Review and Refactor Quiz

1. What is the benefit of extracting a repeated literal like 'Closed Won' into a named constant?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

The final lesson steps back and reviews the whole feature — trigger, handler, and service class together — checking it against everything this course has taught about clean, bulk-safe, well-structured Apex.