3

Lets say I'm trying to model a variety of objects that are virtually identical, the only difference being their class variables. Am I better off creating one boilerplate class and just calling the constructor with the specific variables, or creating a lot (in this case 100+) of essentially identical classes and hard coding their class variables (which will never change)?

To be clear:

  • Identical methods
  • Identical class variable names but different values
  • Identical intended functionality

To be clear, my language (Python) doesn't support interfaces that I'm aware of - some of the other similar questions seemed to get answers recommending that. The other option that seems to make sense to me would be to create a base class with each field and method and then create a bunch of unique child classes that contain their unique values.

The amount of work to create all the child classes isn't significant to me - I've written a program that quickly parses a text file (that has all the unique values) and can write all the classes to a .py file quickly and easily. Making changes down the road is rather easy for the same reason.

(somewhat) Related questions

7
  • Why not just read the values from the text file at run time? Aug 18, 2014 at 4:19
  • @WinstonEwert While I'd have to actually run some time checks, I'm assuming the overhead from the IO every time would be more than having some extra classes. It is an option though. Aug 18, 2014 at 4:40
  • You'd only need to read the data file in once at the beginning of the program and store the data somewhere. IO of reading a class of files and reading your text file should be pretty similar. Aug 18, 2014 at 4:42
  • 1
    What you have just described are instances of a class. Not different classes. Should I create separate subclasses for each employee that has a different name. e.g. EmployeeBob, EmployeeTom, EmployeeAlice...etc?
    – Dunk
    Aug 18, 2014 at 18:50
  • 1
    @Danno:I know. That is exactly what my example showed. I only changed the domain in order to make it readily apparent how absurd the subclass idea is. Choice 1: employee1 = new Employee("Bob", "Smith"); Choice 2: employee2 = new EmployeeBobSmith(); employee3 = new EmployeeTomJones(); employee4 = new Employee AliceBrown(). That is exactly the question you are asking and at least to me, I can come up with tons of problems with choice 2. In particular, what happens when you add another attribute that is worthy of being part of the class name?
    – Dunk
    Aug 19, 2014 at 17:36

2 Answers 2

4

100+ almost identical classes is crazy. Classes are organizational structures. Having dozens of them with almost no variation does't seem useful. It's over-specialization.

It sounds very much like you need a single class with a shared lookup-table that defines the shared info. Something like:

from collections import namedtuple

Lang = namedtuple('Lang', 'style points creator')

LANG_FORM = {
    'Python': Lang('dynamic', 84, 'Guido van Rossum'),
    'Java':   Lang('static', 65, 'James Gosling'),
    # ...
    'Pascal': Lang('static', 33, 'Niklaus Wirth')
}

class Protean(object):

    def __init__(self, name, form):
        self.name = name
        self.form = form
        self.lang = LANG_FORM[form] # optional

    # methods here

Here LANG_FORM can be a global variable (quasi-constant), or it could be a class variable. Either way, each instance gets basically just a pointer back to its shared info (what you refer to as class variables). It doesn't even strictly need that, if you're willing to index LANG_FORM whenever you need it, rather than memorizing it in each instance.

If you want to have each of the instances depict itself based on its form/kind/quasi-class, add a __repr__ method:

def __repr__(self):
    classname = self.__class__.__name__
    return "{0}-{1}({2!r}, {3})".format(classname, self.form, self.name, self.lang.points)

Then:

p = Protean('CPython', 'Python')

print p

Yields:

Protean-Python('CPython', 84)
2

If the behaviour is the same to all classes and they all have the same reason to change, then I'd probably not subclass at all.

It is a matter of instantiation and configuration - it only affects the usage of the class but not the structure. So basically we are talking about trade offs in the usage of the class and its maintenance.

If you have an easy time instanatiating your objects from a datasource like you use for generating the classes, then why not do so? If the constructor only takes three easy to remember or easy to look up arguments, why subclass? There's no reason to create so many classes.

Generating the classes on the other hand can turn out to be harder maintenance, as you need to maintain the base class and also the generators if for some reason - and contrary to your expectations - things change.

I can only think of one reason to opt for the subclasses: When it makes your API significantly simpler and your code easier to understand. For example when your class is instantiated very often by the programmer and it's easier for her/him to remember the classname instead of a hard to remember qualifier for the data that is to be fetched from the datasource. Or if you absolutely can not fetch the data with a qualifier and the programmer has to lookup or calculate a lot of values to instantiate the class. IMHO that's some sort of convention over configuration then and can really turn out to be an API enhancement - especially if the subclass is the natural place to search for the values if a programmer needs to look them up. But in many cases you should also be able to adjust your data source format in a way that humans can easily interact with it.

Another reason to generate sublasses is when - also contrary to your expectations - you realize that you will need enhanced behaviour on at least some of them.

To be clear, my language (Python) doesn't support interfaces that I'm aware of - some of the other similar questions seemed to get answers recommending that.

That is because dynamically typed language don't need interfaces. Mose use duck typing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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