Designing Interfaces

N

Novice

This is actually a discussion that Lew and I started in the "Aspect
Qustions" thread but it is so unwieldy know that I'm pushing it into a
new thread.

Here's the context so that everyone knows what the subject of discussion
is. While this started as sort of a side conversation between Lew and I,
as far as I'm concerned, anyone who has something to contribute is more
than welcome to jump in on this. Even if you say the same thing as
someone else, your wording might be more effective in making me see the
point than the way the other person said it. Anyway, here's where we were
in the other thread.

What are the overall modules? For example: "Obtain resume from single
file", "Export to format X", "Generate references document", ...

At the moment, I have a main program that simply generates each document
in turn. It's called ResumeFileGenerator. Its constructor gets the
resource bundle that drives the creation of the resumes. Each resume
format is generated by a separate class and is passed an object that
represents the data needed in the resume and the path and file name to be
generated by that class. Then, the supporting documents are each
generated by their own separate classes. They too are told the path and
file name that should be generated but are not passed the resume object
since they don't need it.
Sorry, "list" type?

Sorry, that was sloppy shorthanding on my part. I meant the
ResourceBundles that are based on ListResourceBundles, as shown at
http://docs.oracle.com/javase/tutorial/i18n/resbundle/list.html, versus
the "text" and "message" types which are basically properties files. Some
of my data consists of arrays like the list of editors I've used and the
list of word processing programs I'm familiar with so the simpler
property file type resource bundles don't work well for that.
What interface? What method?

It's an interface I created as opposed to one that is in the API. It is
called ResumeFileWriter and has one empty method in it called
writeResume. It has two parameters, a String that identifies the path and
name of the file to be written and a Resume object that refers to the
data contained in the Resource Bundle. Each of the classes that writes an
actual resume (as opposed to a supporting file) implements it.

It's entirely likely that this interface should do a lot more than it
currently does. That's why I'm very curious to get your take on this.

I've also got an abstract class called ResumeFileCreator. It has four
concrete methods: deleteFile, openOutputFile, closeOutputFile and
getGeneratedBy (which simply generates a string containing the name of a
class and the current date and time which I use to create comments in
each of the files that are written). Each of the classes that writes a
resume or supporting file subclasses ResumeFileCreator.
We'll go step by step.

Works for me :)
 
A

Arved Sandstrom

This is actually a discussion that Lew and I started in the "Aspect
Qustions" thread but it is so unwieldy know that I'm pushing it into a
new thread.

Here's the context so that everyone knows what the subject of discussion
is. While this started as sort of a side conversation between Lew and I,
as far as I'm concerned, anyone who has something to contribute is more
than welcome to jump in on this. Even if you say the same thing as
someone else, your wording might be more effective in making me see the
point than the way the other person said it. Anyway, here's where we were
in the other thread.




At the moment, I have a main program that simply generates each document
in turn. It's called ResumeFileGenerator. Its constructor gets the
resource bundle that drives the creation of the resumes. Each resume
format is generated by a separate class and is passed an object that
represents the data needed in the resume and the path and file name to be
generated by that class. Then, the supporting documents are each
generated by their own separate classes. They too are told the path and
file name that should be generated but are not passed the resume object
since they don't need it.



Sorry, that was sloppy shorthanding on my part. I meant the
ResourceBundles that are based on ListResourceBundles, as shown at
http://docs.oracle.com/javase/tutorial/i18n/resbundle/list.html, versus
the "text" and "message" types which are basically properties files. Some
of my data consists of arrays like the list of editors I've used and the
list of word processing programs I'm familiar with so the simpler
property file type resource bundles don't work well for that.



It's an interface I created as opposed to one that is in the API. It is
called ResumeFileWriter and has one empty method in it called
writeResume. It has two parameters, a String that identifies the path and
name of the file to be written and a Resume object that refers to the
data contained in the Resource Bundle. Each of the classes that writes an
actual resume (as opposed to a supporting file) implements it.

It's entirely likely that this interface should do a lot more than it
currently does. That's why I'm very curious to get your take on this.

I've also got an abstract class called ResumeFileCreator. It has four
concrete methods: deleteFile, openOutputFile, closeOutputFile and
getGeneratedBy (which simply generates a string containing the name of a
class and the current date and time which I use to create comments in
each of the files that are written). Each of the classes that writes a
resume or supporting file subclasses ResumeFileCreator.



Works for me :)
Given all that, I prefer a design where the "resume" objects know how to
do things. If you start thinking of "writers" and "creators" you're
headed towards Martin Fowler's Anemic Domain Model (look it up) and
you're becoming quite procedural.

Sure we've got a whole menagerie of Writers and Readers in core Java
APIs, but these are nuts and bolts _implementation_ classes and
interfaces. "Writers" and "Readers" in your own designs aren't illegal
but you want to think things through carefully before deciding that they
are the best solution.

Start with your domain entity, Resume. Maybe that's what ought to be an
interface. I'm just thinking aloud, this would be my first approach.
What would I want a Resume to be able to do? I suspect it ought to be
able to "write" itself: rather than "writeResume" in
"ResumeFileCreator", maybe you've got "write" in "Resume".

Hmmm, different formats? We'll be thinking design patterns in a bit. But
first off, it seems a reasonable conclusion that Resume, not some file
format library, knows how to convert itself to an abstract layout.
Regardless of whether the final format is PDF or HTML or Word or XML,
the Resume knows how to describe its output layout in terms of headers
and footers and paragraphs and so forth.

The nuances of this are open for vigorous debate, and I'll revisit this
below (see Layout vs Logical Structure).

So for writing a Resume, my first picture would be that when instructed
to write itself, a Resume object can decompose itself into abstract
write sub-operations: write the header, write the body, etc. These in
turn have smaller bits.

What are other choices? We have things like style of resume:
skills-oriented, chronology-oriented etc. Resume can certainly deal with
these (again, see Layout vs Logical Structure below). We also have
things like does the format support pagination, and what is the output
footprint (mobile screen, standard page etc), but this is not a Resume
concern necessarily.

Now, while Resume conceptually also knows everything about how to
convert itself to PDF or HTML or Word, and nothing else does, you don't
want Resume tied to a fixed set of output format choices either.
Besides, from a manageability standpoint you don't want your
implementation class(es) too large.

One idea is the Strategy design pattern. Come "write" time, an instance
of a Resume implementation class is supplied a ResumeWriteStrategy.
After all, it's the client code (presumably), not the Resume object,
that selects the actual output format. ResumeWriteStrategy is an
interface that defines the abstract write operations that have to be
implemented for each format; concrete classes (like
ResumePDFWriteStrategy) actually do the heavy lifting.

Your Resume "write" method now gets a strategy when it's invoked, and as
write works through its abstract layout, it delegates its real writing
to the supplied strategy object.

Let's call the Strategy approach Option A.

Another option would be to have a top-level AbstractResume that does
basic implementation of Resume (the abstract layout in "write", for
example), and concrete resume classes for each output format that know
how to do the specific format. This isn't inherently bad: in either
approach you need to write a new "something" to handle a new output
format, and in any case your client code should only know about Resumes.
Lew keeps on mentioning interfaces as "types" - exposing any and all
incarnations of "resumes" to client code as a Resume, where Resume is an
interface, is an example of this.

This is Option B.

I'd dispute anyone who said that Option A was substantially better than
Option B, or vice versa. The only hitch with Option B is that when you
obtain an instance of Resume, you do actually decide the output format
right then and there. Whereas with Option A you simply have a "resume"
implementation class instance, that doesn't know from formats, and it's
only when the client code decides to have the resume output itself that
the decision needs to be made. Depending on system requirements this
could drive a choice.

Let's return to the Strategy option. You should see the difference here
between your ResumeFileWriter and my ResumeWriteStrategy. Your classes
that implement your "writeResume" may appear to be superficially similar
to what my strategy classes do, but there is one major difference: in
your design the resume is a dumb object. This equates to saying that
Resume doesn't know about its logical structure. Well, sure it does.

** Layout vs Logical Structure **

Now, logical structure isn't actually layout either, so as promised I'll
revisit this. We originally have the single "write" method knowing how
to layout at the abstract level. It uses logical structure elements to
do this. Resume (implementations) should be loaded with logical
structure, describing all the data that goes into a CV in an OO kind of way.

Now, there are really 2 output issues, not just one. There is layout,
and then there is output format. With a single "write" method in Resume
you've hardwired to one layout, and odds are you'd like to be flexible
here too. As mentioned before, Resume might want to switch between
skills-oriented layout and chronology-oriented layout, as an example.

There are also *style* type layout decisions: formal vs informal, for
example.

One can safely assume that these are all client code decisions. Not only
that, they can combine. You might have formal+skills, or
formal+chronology, or informal+chronology. So how to handle this? Adding
subclasses in Option B doesn't look appealing -
InformalChronologyHtmlResume is very klunky. I wouldn't want a
ResumeFormalSkillsPDFWriteStrategy either. :)

Ultimately more interfaces. Interfaces in Java are the only "mixin"
we've got. Maybe we continue the strategy idea, and supply more of them
when we call "write".

** Summary **

None of this is authoritative, and I don't even claim it's the best way
to do things. It's my thought process today, and 2 weeks from now it
might be slightly different.

Usually what I do, well before I reach this point, is to code as I think
design. Nobody needs the "last mile" code at this point, which is to
write out the actual PDF or HTML into files or to some other stream;
you're just playing with interfaces and stubbed implementation classes,
and some simple client code.

Think other design patterns too. I've called out just one, and it's not
necessarily the only one or even the best one. There are a number of
other structural and behavioural patterns that should be thought of, and
examined, in this problem.

This is actually an excellent problem for learning Java and OO, so
leverage the hell out of it. :) What I've set out here is one example
of a train of thought. If you remember anything from this, it's in my
first paragraph: avoid value objects that are weak in behaviour. Lots of
Java code that you will encounter is actually very procedural, not
really OO at all, so be careful about what you seize on as examples.

AHS
 
J

Jeff Higgins

This is actually a discussion ...

import java.io.Writer;
import java.util.Locale;
public interface Resume {
Writer write(Template t, Locale l);
}

public interface Template {
Element root();
}

// ElementVisitor ana Element
// swiped from javax.lang.model

public interface ElementVisitor<R, P> {
/**
* Visits an element.
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visit(Element e, P p);
}

import java.util.List;
import java.util.Locale;
public interface Element {
<R, P> R accept(ElementVisitor<R, P> v, P p);
AttributeSet attributes();
AttributeSet attributes(Locale l);
Element enclosingElement();
List<? extends Element> enclosedElements();
}

// AttributeSet
// swiped from javax.swing.text

public interface AttributeSet {
public int count();
public Object get(Object key);
public boolean contains(Object name, Object value);
public boolean containsAttributes(AttributeSet attributes);
public AttributeSet parent();
}
 
J

Jeff Higgins

import java.io.Writer;
import java.util.Locale;
public interface Resume { Writer write(Template t);
Writer write(Template t, Locale l);
}

public interface Template {
Element root();
}

// ElementVisitor ana Element
// swiped from javax.lang.model

public interface ElementVisitor<R, P> {
/**
* Visits an element.
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visit(Element e, P p);
}

Oops. skip the AttributeSet, you can include it in the P parameter.
 
J

Jeff Higgins

Nah, skip all of that. I've decided to store my resume data in an
instance of Candidate[1] and my resume output style in a stylesheet and
transform via JAXP.

HR-XML Standards 3.2 Release
<http://www.hr-xml.org/>
 
N

Novice

[snip]

I think I need to put these questions aside for a bit and focus on more
pressing programming needs, especially logging and error handling. In fact,
that's pretty much what I HAVE been doing for the last several days ;-)

I probably shouldn't have started this thread until I had a bit more time
to devote to it. I have read the responses Arved and Jeff made so far and
found them interesting and thought-provoking. But I don't have the time
just now to really follow through properly on the theme of this discussion.

I'll start another thread on design of interfaces when I have more time.

Thanks to Jeff and Arved for their ideas!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top