170 likes | 302 Views
Decorator. COMP 401, Spring 2014 Lecture 15 3 / 6 /2014. A motivating example: SongLog. Goal: Create an object to represent a song “log” (i.e., record of what songs were played) Provide ability to answer queries about songs played. Functionality: void recordInLog ( Song s)
E N D
Decorator COMP 401, Spring 2014 Lecture 15 3/6/2014
A motivating example: SongLog • Goal: • Create an object to represent a song “log” (i.e., record of what songs were played) • Provide ability to answer queries about songs played. • Functionality: • void recordInLog(Song s) • void recordInLog(Song s, Date time) • Date lastPlayed(Song s)
Date • Java’s class for dealing with Date • Represents a point in time down to millisecond precision. • Separate classes for formatting and calendar operations. • Calendar • DateFormat • Provides a number of pre-defined formats • SimpleDateFormat • Allows you to construct customized formats • See Date tutorial on Oracle site for more.
SongLog version 1 • lec15.v1 • Strategy: • Maintain two lists • One for songs • One for dates
SongLog v1 critique • It works, but not as clean as it could be. • Why?
Decorator Pattern • Useful when you want to add additional state or functionality to a class without formally subclassing. • When might that be? • Additional state/functionality is auxiliary to object’s main purpose. • Additional state/functionality is local to a collection or some other class encapsulating the object. • SongLog for example. • As a way of emulating multiple inheritance • Want to build up an object with subsets of different functionality. • Decorator pattern is a form of delegation.
Decorator Pattern Recipe • Setting up: • Start with original interface. • If decorating a class without an interface, refactor original class to have an interface. • In our example: • Song (this is the interface) • SongImpl
Decorator Pattern Recipe • Step 1: • Extend interface, declaring additional functionality. • In our example: • LoggedSong • Date getDate();
Decorate Pattern Recipe • Step 2: • Create class that implements decorated interface. • Constructor is given an instance of the original, undecorated type. • May also need to provide any additional state information needed for decorated behavior. • Delegate original interface methods to the encapsulated original object. • Provide implementations for additionaly decorated behavior. • In our example: • LoggedSongImpl • public LoggedSongImpl(Song s, Date d) • Date getDate()
SongLog v2 • This version decorates the songs as logged songs and then stores them.
Decorator Avoids Subclassing • Decorator applies to interfaces (not classes). • Caveat: as I am teaching it here. • It’s the interface that is being decorated. • Subinterfacing is distinct from subclassing • Both are types of inheritance, but not the same • This is why we encounter statements like: • Decorators provide a flexible alternative to subclassing for extending functionality. • See Decorator in Java tutorial from Abhi On Java link in readings. • This is why we needed Song to be an interface with an accompanying class (i.e., SongImpl) implementation.
Decorator Illustrated interface I { void do_something(); } class C implements I { void do_something() { ... } } class DC implements DI { private I wrapped_i; public (I i_obj) { wrapped_i = i_obj; } void do_something() { wrapped_i.do_something(); } void do_more() { ... } } interface DI extends I { void do_more(); }
Unwrapping the Decorator • When you decorate an existing object, you are creating a new object. • Originalobject is encapsulated inside. • Might need to have the ability to “undecorate” the object. • For example, if we need to give it back to someone who expects the original. • lec15.v3
Undecorating • Provide method to get back original in decorated interface. interface I { void do_something(); } class C implements I { void do_something() { ... } } class DC implements DI { private I wrapped_i; public (I i_obj) { wrapped_i = i_obj; } public I getWrapped() { wrapped_i; } void do_something() { wrapped_i.do_something(); } void do_more() { ... } } interface DI extends I { void do_more(); I getWrapped(); }
Quienes Mas Macho? for (inti=0; i<f.getWidth(); i++) { for (intii=0; ii<f.getHeight(); ii++) { double v = f.getPixel(i,ii).getRed(); red_sum += v; v = f.getPixel(i,ii).getGreen(); green_sum += v; v = f.getPixel(i,ii).getBlue(); blue_sum += v; } } for (int x=0; x<f.getWidth(); x++) { for (int y=0; y<f.getHeight(); y++) { Pixel p = f.getPixel(x,y); red_sum += p.getRed(); green_sum += p.getGreen(); blue_sum += p.getBlue(); } } • Create local variables to represent subexpressions if they are going to be used more than once. • Declare / initialize variables close to where they are going to be used and limit scope as much as possible. • Don’t reuse variables for distinctly different purposes. • Avoid creating a variable if it’s value is only going to be used once. • Caveat: may want to do this if it improves readability of long line of code. • Use 1-letter, or very abbreviated variable names only if their use is limited to a few lines of code or as an indexing variable. Even then, try to make meaningful choices.
Quienes Mas Macho? class IndirectFrame { private Frame frame; … class IndirectFrame { private Frame source; • Use names that reflect their purpose, not their type.
Quienes Mas Macho? class Class { private int width; private inty_offset; public String toString()… public static void helper() … private int Height; private intxOffset; public Class() … public void doSomething() … public static void other_helper() … class Class { private int width; private intheight; private intx_offset; private inty_offset; public Class() … public void doSomething() … public String toString()… public static void helper() … public static void other_helper() … • Be consistent with naming conventions. • Don’t mix instance variable declarations within method definitions. • Put them at the top. • Keep static methods together. • Put constructors first. • Right after instance field declarations. • Lead with subclass-specific methods, then overrides.