A web server broadcasts lifecycle events to its installed plugins. The logger, cache, and metrics plugins each care about two moments in that lifecycle—but none cares about all three.
Refactor the starter's PluginHost design without changing its public behavior:
PluginHost() creates a host with no plugins.int addLogger(), int addCache() and int addMetrics() each install a plugin and return its index.int pluginCount() returns how many plugins are installed.String pluginName(int index) returns "logger", "cache" or "metrics", or "UNKNOWN" for an out-of-range index.String hooksFor(int index) returns the moments that plugin takes part in, from start, request and stop, in that order and joined by commas. An out-of-range index returns "UNKNOWN".String[] fireStart(), String[] fireRequest(String path) and String[] fireStop() each notify the plugins that take part in that moment, in install order, and return what they produced.The three plugins take part in different moments:
logger takes part in start and stop, producing "logger started" and "logger stopped".cache takes part in start and request, producing "cache warmed" and "cache checked <path>".metrics takes part in request and stop, producing "metrics counted <path>" and "metrics flushed".A plugin that does not take part in a moment contributes nothing to that result, not an empty entry.
The legacy starter already produces all of these results. Its problem is structural: one LifecyclePlugin interface forces every plugin to implement all three hooks, so unused hooks return empty strings and the host filters them out.
Split that broad interface into focused lifecycle contracts. PluginHost is the only type the tests call, but a plugin should no longer have to acknowledge a moment it has no interest in.
Input:
Output:
Explanation: The logger takes part in start and stop, the cache in start and request. Each phase includes only the plugins that participate in it, in install order.
Input:
Output:
Explanation: Metrics has nothing to say at startup, so fireStart returns an empty result rather than a placeholder entry.
1 <= path.length <= 3020 plugins are installed on one host.100 calls in total are made across all methods.Full marks when start, request and stop are three separate contracts and each plugin implements only the ones it participates in, so no class has a hook method it leaves empty. Lose points heavily when one plugin contract declares all three hooks, or when a plugin returns a placeholder for a moment it does not care about.
Full marks when each fire method walks the plugins in install order and includes only those satisfying that hook, and `hooksFor` reports membership using the same test. Lose points when a fire method branches on the plugin name, or when `hooksFor` returns a value the fire methods contradict.
Full marks when hook names are listed in start, request, stop order, an out-of-range index returns `UNKNOWN` from both index methods, and a plugin with no participants in a phase yields an empty result rather than a placeholder. Lose points when a plugin holds a reference to the host or for printing to stdout.
Passing every test is not enough on its own. A submission is accepted only when the design also clears the bar.
| Call | Returns |
|---|---|
| new PluginHost() | null |
| addLogger() | 0 |
| addCache() | 1 |
| fireStart() | ["logger started","cache warmed"] |
| fireRequest("/home") | ["cache checked /home"] |
| fireStop() | ["logger stopped"] |
| hooksFor(1) | "start,request" |
The logger takes part in start and stop, the cache in start and request. Each phase includes only the plugins that participate in it, in install order.
Run checks these cases. Submit also runs a larger hidden set.

