The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Follow OO design rules to the limit or not?!

Hello!
Imagine, if you will, that you have these classes::

class Process
private ID as long
private Date as date
.....

class ProcessMaterial
private LineID as long
private Item as clsItem //the product being used
private Technician as clsTechnician //who used the product
private Reason as clsReason //the reason
private DebitType as clsDebitType //who pays for it
....

Now, one process may have hundreds of lines and in most cases, I only need the Reason.ID,Reason.Description, Technician.ID, Technician.Description, DebitType.Id, DebityType.Description.

OO tell us we should define Technician, Reason, DebitType as classes (in this case, anyway), but is it so wrong (for low memory usage) to define Technician as
private TechnicianID as long
Private TechnicianDescription as description.

Instead of loading the Technician, Reason , etc, objects which will bring everything with them (other class properties), why not, just define it as ID, Description?

It's not really OO, I know! But shouldn't we try to make our application as efficient as possible?!

I just don't know...
Any suggestions, complaints :) would be greatly appreciated.
Jorge Carvalho Send private email
Friday, August 26, 2005
 
 
Because you should not make more than one copy of data, if you ever want to keep it up to date. Once you duplicate anything, you have got another responsibility (to ensure that both copies are up to date). It's better to spend a little more speed/memory than to increase complexity of the program: complexity is the single most expensive thing you can add, it should be used sparingly.
ping?
Friday, August 26, 2005
 
 
I used a form of lazy initialization when confronted with a similar problem:

Scenario: Client application obtains (from server) a list of objects from which the user will have to choose one or more elements (imagine a menu or multiple-choice drop list).

After the user has picked the objects s/he is interested in, the client will need a more complete description of the objects to work on them (and possibly require some kind of lock if the object represent some finite resource).

You may use a hierarchy like:
BasicElement (having just ID and description, to be shown in the GUI list)
 HalfBakedElement extends BasicElement (has some extra features and method like, price, in-store availability or colour or whatever, used when an explicit user operation requires more data to process)
  FullBlownElement extends HalfBackedElement (has the full implementation with all data and all methods).

Note:
1) I gave an example with three distinct levels of "completeness", two may be enough, or more may be required (though I doubt it).
2) My example involves a client/server, but in reality something similar could be useful for a report, where having full-blown objects with lots of logic and methods would be overkill when you just need an ID and a description.

You will need factories or "manager" objects that given an ID can read from persistence either a Basic, HalfBaked or FullBlownElement.
Paolo Marino Send private email
Friday, August 26, 2005
 
 
Yes, you should limit all design rues.
son of parnas
Friday, August 26, 2005
 
 
You can make it more efficient and still be good OO by using Proxy pattern there.

Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
Denis Krukovsky
Friday, August 26, 2005
 
 
You're hitting the object-relational mapping problem here.

OO "requires" us to work with objects. Relational works with sets of data structures (tuples)

Here's an example of what's going on inside an ORM:


import java.util.Iterator;
import java.util.List;

interface Persistent {
    void remove() throws ExceptionConstraintViolation;
    void update() throws ExceptionConstraintViolation;
}

interface PersistencyManager {
    void create(Persistent newObj) throws ExceptionConstraintViolation;

    boolean restore(String sql, Persistent newObj);

    Iterator restore(String where, Class clazz);

    void update(Persistent obj) throws ExceptionConstraintViolation;

    void update(String set, String where, Class clazz) throws ExceptionConstraintViolation;
   
    void remove(Persistent victim) throws ExceptionConstraintViolation;

    void remove(String where, Class clazz) throws ExceptionConstraintViolation;
   
    long count(String where, Class clazz);
};

class CutPoints {
    static public PersistencyManager getPOM() {
        // TODO implement your persistent object manager.
        return null;
    }
}

class ExceptionConstraintViolation extends Exception {
};

class Master implements Persistent {
    private long id;

    //  ==== Master semantics ====
    // Creating memory stub.
    private Master() {
    }

    // Create semantics
    public Master(long id) throws ExceptionConstraintViolation {
        this.id = id;
        CutPoints.getPOM().create(this);
    }

    // Find semantics
    public static Master find(long id) {
        Master result = new Master();
        if (!CutPoints.getPOM().restore("ID=" + Long.toString(id), result)) {
            return null;
        }
        return result;
    }

    public Iterator find(String where) {
        return CutPoints.getPOM().restore(where, Master.class);
    }

    // Delete semantics
    public void remove() throws ExceptionConstraintViolation {
        CutPoints.getPOM().remove(this);
    }

    // Update semantics
    public void update() throws ExceptionConstraintViolation {
        CutPoints.getPOM().update(this);
    }

    // ===== Detail semantics ========
    // Retrieve methods
    public Iterator getDetails() {
        return getDetails("MASTER_ID=" + Long.toString(this.id));
    }

    public Iterator getDetails(String where) {
        return Detail.find("MASTER_ID=" + Long.toString(this.id) +
                " AND " + where);       
    }

    // Assoc methods
    public void addDetail(Detail detail) throws ExceptionConstraintViolation {
        insertDetail(0, detail);
    }

    public void addDetails(List details) throws ExceptionConstraintViolation {
        insertDetails(0, details);
    }

    public void insertDetail(long index, Object data)
            throws ExceptionConstraintViolation {
        long maxIndex = getDetailCount();
        if(index >= maxIndex) {
            appendDetail(data);
        } else {
            resequenceDetails(index, 1);
            new Detail(this.id, index, data);
        }
    }

    public void insertDetails(long index, List detailsData)
            throws ExceptionConstraintViolation {
        long maxIndex = getDetailCount();
        if(index >= maxIndex) {
            appendDetails(detailsData);
        } else {
            long len = detailsData.size();
            resequenceDetails(index, len);
           
            long count = index;
            for(Iterator iter = detailsData.iterator(); iter.hasNext(); ) {
                Object data = iter.next();
                new Detail(this.id, count, data);
                count++;
            }
        }
    }

    public void appendDetail(Object data) throws ExceptionConstraintViolation {
        new Detail(this.id, getDetailCount() + 1, data);
    }

    public void appendDetails(List detailsData) throws ExceptionConstraintViolation {
        long count = getDetailCount() + 1;
        for(Iterator iter = detailsData.iterator(); iter.hasNext(); ) {
            Object data= iter.next();
            new Detail(this.id, count, data);
            count++;
        }
    }

    // Dissassociation methods
    public void removeDetail(Detail victim) throws ExceptionConstraintViolation {
        long index = victim.getSequence();
        victim.remove();
        resequenceDetails(index, -1);
    }

    public void removeDetails(String where) throws ExceptionConstraintViolation {
        removeDetails(where);
        resequenceDetails();
    }

    public void removeDetails(List victims) throws ExceptionConstraintViolation {
        for(Iterator iter = victims.iterator(); iter.hasNext();) {
            ((Detail)iter.next()).remove();
        }
        resequenceDetails();
    }

    public void removeAllDetails() throws ExceptionConstraintViolation {
        Detail.removeAllDetails(this.id);
    }
   
    // Other
    public long getDetailCount() {
        return CutPoints.getPOM().count("MASTER_ID=" + Long.toString(this.id), Detail.class);
    }
   
    private void resequenceDetails(long from, long offset) throws ExceptionConstraintViolation {
        Detail.resequenceDetails(this.id, from, offset);
    }
   
    private void resequenceDetails() throws ExceptionConstraintViolation {
        long count = 0;
        for(Iterator iter = getDetails(); iter.hasNext(); ) {
            Detail detail = (Detail)iter.next();
            detail.setSequence(count);
            detail.update();
            count++;
        }
    }
}

class Detail implements Persistent {
    protected long masterId;
    protected long sequence;

    private Detail () {};
   
    public Detail(long masterId, long sequence, Object data) throws ExceptionConstraintViolation {
        this.masterId = masterId;
        this.sequence = sequence;
        // ... data goes here
       
        CutPoints.getPOM().create(this);
    }
   
    public static Detail find(long masterid, long sequence) {
        Detail result = new Detail();
        if(!CutPoints.getPOM().restore("MASTER_ID=" + Long.toString(masterid) + " AND " +
                "SEQUENCE=" + Long.toString(sequence), result)) {
            return null;
        }
        return result;
    }
   
    public static Iterator find(long masterid) {
        return find("MASTER_ID=" + Long.toString(masterid));
    }
   
    public static Iterator find(String where) {
        return CutPoints.getPOM().restore(where, Detail.class);
    }
   
    // Delete semantics
    public void remove() throws ExceptionConstraintViolation {
        CutPoints.getPOM().remove(this);
    }

    // Update semantics
    public void update() throws ExceptionConstraintViolation {
        CutPoints.getPOM().update(this);
    }   
    public long getSequence() {
        return this.sequence;
    }
   
    public void setSequence(long sequence) {
        this.sequence = sequence;
    }
   
    public static void resequenceDetails(long masterid, long from, long offset) throws ExceptionConstraintViolation {       
        CutPoints.getPOM().update("SEQUENCE=SEQUENCE + " + Long.toString(offset),
                "MASTER_ID=" + Long.toString(masterid) + " AND " +
                "SEQUENCE >= " + Long.toString(from),
                Detail.class);       
    }   

    public static void removeAllDetails(long masterid) throws ExceptionConstraintViolation {
        removeDetails("MASTER_ID=" + Long.toString(masterid));
    }
   
    public static void removeDetails(String where) throws ExceptionConstraintViolation {
        CutPoints.getPOM().remove(where, Detail.class);
    }
}

This maps on the following table structures:

MASTER:
ID


DETAIL:
MASTER_ID
SEQUENCE
Dino Send private email
Friday, August 26, 2005
 
 
You should design the classes to support the application that you need to write.

If you where writing classes to define/control say an elevator - would you include floor as a number or would you include a floor object that told you where all the desks are.

Perhaps if you had an elevator that could open in 2 directions on certain floors you would expand to have a floor object to contain that extra information.

Sunday, August 28, 2005
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz