Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Sorry for the confusing title of this question, but I can't think of an exact way to word it. I have a very long class with hundreds of similar methods and I am trying to find a way to construct it without so many lines of code and so many DRY violations. Each time I add a new property to my class I have to write two methods and update the copy function. There are already over 80 properties.

Currently this class is written in Java but I was wondering if it were written in C++ if it could be done more efficiently. I think it can be done using preprocessor x-macros like described in wikipedia here but I am not a fan of macros and would prefer to do it using the language itself if possible.

Here is a simplified snippet of my Java class:

class InMemoryPerson implements Person {
    private HashMap<String,Object> values = new HashMap<String,Object>();

    public InMemoryPerson copyFrom(Person p) {
        setName(p.getName());
        // more code to copy all the properties...
    }

    public void setName(String name) {
        values.put("name",name);
    }

    public String getName() {
        return (String)map.get("name");
    }

    // many more methods for all the different properties...
    // each method may have a different type
}
share|improve this question
8  
You might want to rethink your design completely. 80 member variables that all need accessors sounds wrong. –  Mat May 2 '13 at 6:33
1  
Then what do you need 80 methods for? –  Mat May 2 '13 at 6:45
4  
Don't worry about a bit of DRY violation when you're doing three truckloads of SRP violations (you are, no sane single-responsibility class would have 80 properties). –  Joris Timmermans May 2 '13 at 6:57
2  
maybe you need this steve-yegge.blogspot.ca/2008/10/universal-design-pattern.html –  jk. May 2 '13 at 9:19
1  
@Steve314 - by the same logic describing the universe is a single responsibility too. –  Joris Timmermans May 2 '13 at 11:42
show 11 more comments

5 Answers 5

up vote 7 down vote accepted

In C++ this is possible to do, using old style C macros.

// method is get/set method suffix
// name is the key in hash table
#define POINTER_PROPERTY(type,method,name) \
        type get##method(){return (type)_pointer_map[name];} \
        void set##method(type value){_pointer_map[name]=value;}

class InMemoryPerson : Person {
public:
    POINTER_PROPERTY(char *,Name,"name")

private:
    std::map<const char *,void *> _pointer_map;
};

Value types would have to be handled by different macro and (easiest way) by different hash for each type. Not sure though this code is more readable and manageable than 80 different get/set functions.

share|improve this answer
    
That is exactly the kind of solution I had in mind. And I believe this is easier to manage. –  Doc Brown May 2 '13 at 9:09
add comment

In c++ it should be quite simple. Throw out setters and getters, and member variables, and use default copy constructors to copy objects of this class. Something like this :

struct Person
{
    Person() : name("default name")
    {}

    std::string name;
    // etc
};

Person a;
// set fields of person a
Person b;
// set fields of person b
b = a;
share|improve this answer
    
Because I am implementing an interface this is not possible, there are several other implementations and my class must be compatible with the interface. Also there is value to storing this data in a HashMap so I need to keep that as well. –  satur9nine May 2 '13 at 6:42
3  
@satur9nine: C++ and Java are sufficiently different that transliterating your Java class to C++ will not give satisfactory results. There is no free lunch here. –  Bart van Ingen Schenau May 2 '13 at 7:03
    
@satur9nine So, your interface has 180 methods? :) Sorry for laughing, but I find it quite amusing. –  BЈовић May 2 '13 at 7:06
    
@BartvanIngenSchenau: I think you are missing the point, the current Java solution is not satisfactory, and Java and C++ are sufficiently similar that the direct C++ transliterated version of this code won't be satisfactory, too. However, I think I would use macros for this case. –  Doc Brown May 2 '13 at 8:46
2  
@DocBrown: I don't think I entirely missed the point. There are multiple ways to look at it. Given the constraints mentioned in the comment, I don't think there is a satisfactory solution in either Java or C++, partially for the same reasons partially for different reasons. –  Bart van Ingen Schenau May 2 '13 at 8:53
show 2 more comments

This isn't really an answer because of the following constraint from a comment...

Because I am implementing an interface this is not possible, there are several other implementations and my class must be compatible with the interface.

However, it might be an answer for someone else, so...

One option is to provide one getter and setter for each type and to use a parameter to identify which property. I can think of two ways to handle this...

  • Use an enumerate to provide all your property names, and store those properties in an array.

  • Use a separate struct to define all the member data, possibly as a public member of your class. Store the properties using in a private instance of that struct inside your class. Pass member pointers to your getters and setters to identify the property.

Either way allows the getters and setters to handle particular properties specially if needed. The member pointer method is only really mentioned for completeness, though - I'd prefer the enumerate method.

Once you can identify which property using a parameter, you still need different getters and setters for each property datatype. This can be avoided by specifying your getter and setter as member template functions. You still need some checks, though, to ensure that the property for the current call has the correct type. This is actually easier if you used the member pointer approach (the template can require that the member type for the member pointer matches the get/set type) though you'll probably get other problems later.

The most obvious case where something like this is justified is if you're developing a RAD-style tool.

share|improve this answer
add comment

The need for updating the copy function can be avoided by using default copy ctors, as @BЈовић has pointed out. For getters and setters, however, I would bite the bullet and use X-macros (if you don't want to write an external code generator for this).

Whatever you do, you need a place where you associate the hash key "name" with the associated data type String and the corresponding getter/setter names (the names of these methods may follow a strict convention, but AFAIK there is no easy uppercase/lowercase conversion within the preprocessor). So defining that list using X-macros will at least give you a very compact and easy-to-extend solution (EDIT: see @petrch's post for an example).

share|improve this answer
add comment

There was a three-part series on Embedded.com about X-macros, that gives a cleaner result than what the Wikipedia article describes.

#define EXPAND_AS_ENUMERATION(a,b) a,
#define EXPAND_AS_JUMPTABLE(a,b) b, 
#define STATE_TABLE(ENTRY)     \
        ENTRY(STATE_0, func_0) \
        ENTRY(STATE_1, func_1) \
        ENTRY(STATE_2, func_2) \
        ...                    \
        ENTRY(STATE_X, func_X)

/* declare an enumeration of state codes */ 
enum{
    STATE_TABLE(EXPAND_AS_ENUMERATION)
    NUM_STATES
}

/* declare a table of function pointers */ 
p_func_t jumptable[NUM_STATES] = {
    STATE_TABLE(EXPAND_AS_JUMPTABLE)
}:

Here's Part 1.

Here's Part 2.

They blew the in-article link to Part 3. Here it is.

They require you to create a free account and profile, so they can spam you, if you want to read more than 2 articles per day. (BOO! HISS!) The 2-article limit is a fairly recent change to what used to be a really good site.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.