I Interface Segregation Principle ISP · Quick recall Q&A
2 min readQuick recall Q&A
Components with minimal prop requirements can be used in more contexts. A <Button onClick={...}> is more reusable than <Button form={formObject}> that requires a fat form interface.
- Accept props they never use
- Pass entire objects when they only need one or two properties
- Implement optional methods that throw "not supported" errors
- Have
anyor overly broad types to avoid interface constraints
Components that:
Custom hooks should accept focused dependencies:
// Bad - hook requires entire user service
function useUserName(userService: UserService) {
// Only uses getName method
}
// Good - hook requires only what it needs
function useUserName(getName: (id: string) => string) {
// Minimal dependency
}Inject only the interface you need. Angular allows providing multiple interfaces with the same implementation:
providers: [
UserService,
{ provide: ReadableUserService, useExisting: UserService },
{ provide: WritableUserService, useExisting: UserService }
]
// Components inject what they need
constructor(private users: ReadableUserService) {}- Change together (all CRUD operations might stay together)
- Serve the same use case
- Have similar client needs
Segregate by cohesive responsibilities, not per method. Group operations that:
Don't create one interface per method unless methods truly serve different concerns.
Smaller interfaces mean simpler mocks:
// Easy to mock
const mockPlayable: Playable = {
play: jest.fn(),
pause: jest.fn(),
stop: jest.fn()
};
// vs mocking a 20-method interfaceYes. Too many tiny interfaces create noise:
// Over-segregated
interface Clickable { onClick(): void; }
interface Hoverable { onHover(): void; }
interface Focusable { onFocus(): void; }
// Better - cohesive interaction interface
interface Interactive {
onClick(): void;
onHover(): void;
onFocus(): void;
}
Balance between focused and practical.
Split large contexts into focused ones:
// Bad - single fat context
const AppContext = createContext({
user, setUser,
theme, setTheme,
notifications, addNotification,
settings, updateSettings
});
// Good - segregated contexts
const UserContext = createContext({ user, setUser });
const ThemeContext = createContext({ theme, setTheme });
const NotificationContext = createContext({ notifications, addNotification });
const SettingsContext = createContext({ settings, updateSettings });
// Components consume only what they need
function Avatar() {
const { user } = useContext(UserContext); // Only user context
return <img src={user.avatar} />;
}Create focused selectors and action creators instead of exposing the entire store:
// Bad - component depends on entire state shape
function UserProfile({ state }: { state: AppState }) {
return <div>{state.user.profile.name}</div>;
}
// Good - component depends on minimal data
function UserProfile({ userName }: { userName: string }) {
return <div>{userName}</div>;
}
// Container uses focused selector
const mapStateToProps = (state: AppState) => ({
userName: selectUserName(state) // Minimal interface
});Segregated interfaces enable better tree-shaking. Import only the interfaces/implementations you need:
// Can import and use AudioPlayer without pulling in video codec dependencies
import { AudioPlayer } from '@/players';- Interfaces have single, clear purposes
- Components don't accept props they never use
- Services don't implement methods that throw "not supported"
- Type definitions match actual usage patterns
- No large "god objects" passed around
Check that: