[HD] EECS4302 F26 - 2026-10-08 (Thursdsay) - Lecture 9
Watch on YouTube →
Overview
EECS4302 Lecture 9 develops the Composite pattern in Java through four design attempts, showing how to prevent invalid operations, eliminate duplicated collection code with generics, and handle Java’s restriction against extending multiple classes. It traces recursive price calculation using polymorphism and dynamic binding, then introduces the Visitor pattern through expression-tree operations and the cohesion, single-choice, and open-closed principles.
Key takeaways
- Put child collections and add operations on composite types, not on every Equipment: otherwise Java permits invalid calls such as adding a disk drive to a video card.
- A generic abstract Composite<E> can centralize child-management code for multiple recursive domains, while a domain-specific interface avoids Java’s prohibition on extending two classes.
- A shared price() method enables uniform access: dynamic binding dispatches to direct attribute access for leaves and recursive summation for composites.
- When evaluating a tree, trace both its construction and its recursive method calls; in the example, a cabinet containing a chassis, bus, video card, and disk drive totals 700.
- Declaring every tree operation on every expression class makes adding an operation touch many classes; Visitor is introduced as an alternative for recursive structures.
- Cohesion, the single-choice principle, and the open-closed principle help evaluate whether responsibilities are focused, changes are localized, and syntax or operations should remain extensible.
Chapters
- A vertical line ending in an empty arrowhead represents inheritance; labels such as «abstract» and «interface» correspond to Java class and interface types.
- A horizontal solid-headed arrow represents a client–supplier relationship associated with an attribute.
- For a Student holding a YU card, Student is the client, Card is the supplier, and the arrow can be labeled with the attribute name.
- Design diagrams can show selected important classes and relationships rather than every class in a project’s model package.
- The lecture site provides multiple Composite and Visitor design attempts for analysis and written-test preparation.
- A useful review exercise is to explain why a particular design diagram is valid or flawed.
- The equipment hierarchy models recursive systems such as cabinets, chassis, power supplies, disk drives, and video cards.
- Attempt 1 puts a List<Equipment> children attribute and an add(Equipment) method in the abstract Equipment class.
- Because every equipment inherits add, the design permits nonsensical calls such as adding a video card to a disk drive.
- Attempt 2 moves children and add into CompositeEquipment, so leaf types such as VideoCard no longer expose the add method.
- A call like videoCard.add(diskDrive) now fails to compile, addressing the invalid-operation flaw in Attempt 1.
- Cabinet, chassis, and bus can remain composite types that contain other equipment.
- A separate CompositeFurniture hierarchy would need its own children list and collection operations such as add and remove.
- Equipment and furniture composites would duplicate generic collection-management code even though the operations do not depend on the element type.
- The design smell is addressed by moving common code to one reusable location.
- Attempt 3 introduces a generic abstract Composite<E> so equipment and furniture can reuse collection behavior with different element types.
- The proposed CompositeEquipment would need to extend both Equipment and Composite, which Java does not allow for classes.
- Java supports implementing multiple interfaces, but a class can extend only one class.
- The diamond problem arises when class C inherits from A and B and both define doSomething(), leaving the method call ambiguous.
- Languages without a method-conflict resolution mechanism, including Java, reject multiple class inheritance.
- The lecture notes that resolving conflicts would require extra language syntax, then motivates combining an abstract class with an interface.
- The final design uses a generic abstract Composite<E> to hold children and implement reusable collection operations.
- CompositeEquipment extends Composite<Equipment> and implements the Equipment interface, avoiding multiple class inheritance.
- Composite< Furniture > can reuse the same collection implementation for a separate recursive furniture hierarchy.
- The Equipment interface defines a common price() operation for both base components and composite components.
- Base equipment, such as a video card, returns its stored price attribute directly.
- Composite equipment computes its price by iterating over children and adding each child’s price.
- In the composite price loop, each child is typed as Equipment, but its dynamic type can be base equipment or composite equipment.
- Dynamic binding selects the appropriate price() implementation: attribute access for a leaf, recursive aggregation for a composite.
- This shared interface lets the tree traversal handle mixed component types without explicit type checks.
- The example builds a Bus containing a video card priced at 200, a Chassis containing the bus and a disk drive priced at 500, and a Cabinet containing the chassis.
- Tracing cabinet.price() recursively visits the chassis, then the bus and disk drive, producing a total of 700.
- The same reasoning supports converting between Java construction code and a tree diagram, and small trees can help debug recursive methods.
- The Visitor discussion assumes a recursive structure; an abstract syntax tree is a natural example because expressions contain subexpressions.
- The example language includes integer constants and binary addition nodes with left and right children.
- Potential tree operations include evaluation, pretty-printing in prefix or postfix notation, type checking, and generating C or Python code.
- A naive design declares every operation on the expression interface and requires both constants and composite expressions to implement all of them.
- Adding an operation, such as assembly-code generation, then requires changes to the interface and every expression class.
- The design also risks concentrating unrelated operations such as evaluation, printing, and type checking in an overly broad class.
- Cohesion favors keeping relevant features together rather than collecting unrelated responsibilities in a Superman class.
- The single-choice principle aims to confine a change to one location, or the minimum justified number of locations.
- Duplicated behavior can often be consolidated into a helper method or reusable generic class, as in Composite<E>.
- The open-closed principle frames classes as open for extension and closed for modification, with stability determining which parts should change.
- One option keeps expression syntax open to new language features while treating the supported operation list as fixed.
- The alternative fixes the syntax structure but keeps operations open to additions; the lecture sets up this choice as a basis for the Visitor architecture.
Summary, takeaways, and chapters were generated by AI from the video's transcript and may contain errors. The video belongs to its creator, Jackie Wang.