1

Base Class

I have a class called Remote. This class represents a remote machine and has properties such as ip, hostname, username, and password, as well as methods for transferring files to/from the machine and running code on the machines command line.

Classes Based on Specific VM Providers

I have multiple subclasses of Remote, such as SLRemote, for remote machines running on SoftLayer, VBRemote for machines running in Virtual Box, and potentially in the future I might create other subclasses like an AWSRemote for remote machines running on Amazon Web Services, for example.

These classes have the __init__ method set so that they can take in a few arguments specific to the VM provider and properly find an existing VM, or provision a new VM. Since this may take awhile (IE, SoftLayer takes 2-20 minutes to set up a new VM, depending on image size and network conditions), the __init__ method runs these tasks on a separate thread, and I define a __getattribute__ method which joins the creation thread if it requires that the VM be finished (IE, how can you transfer files to/from the machine if it hasn't finished provisioning?)

Classes Based on Specific Software

I also have classes like DB2Server, WASServer, and FTPServer, which represent remote machines which have specific software installed on them. It seems to me that these classes should also somehow inherit from Remote. Just like Remote, they have an ip, a hostname, a username, and a password. Just like you want to transfer files to a Remote, you want to transfer files to these classes.

Solutions I've Considered

1 - So I want to subclass Remote, right? Well, no, not really, because DB2Server could be installed on a SLRemote or a VBRemote or a AWSRemote, in addition to the generic Remote.

2 - What about multiple inheritance? Again, no, not really, because then I have to write a set of classes for inheriting from each specific VM Provider with the specific software. Plus what if you install multiple things on the same server?

3 - Currently, the design I'm going with is that the classes based on specific software being installed inherit from object (I'm using Python 2), and the __init__ method takes in a remote argument. I've written __getattribute__ and __setattr__ so that in addition to querying the object directly for attributes, it also checks the remote object for those same attributes.

But this is annoying because it requires me to write the exact same __getattribute__ and __setattr__ method in each of my specific software classes, and as my team is growing and more people are having to write classes like these, they're having to come to understand that I'm using these methods and they need to replicate that code, too. It has a bad code smell in it.

4 - As I was writing up what I'm currently doing, this just came to me (the benefits of writing out your problem on Stack Exchange... or a sticky note... or an email... or telling it to someone who knows nothing at all about the problem you're talking about). I could write a base Software class which just takes in a Remote instance and defines the __getattribute__ and __setattr__ methods. Then DB2Server, WASServer, and FTPServer would inherit from that instead.

Other Things that I'm vaguely aware of that might help?

Metaclasses? Aspect Oriented Programming? Mix-ins? These are all concepts that I'm vaguely aware of... would any of these help me with my problem?

What is the right way of doing this? What is the standard and correct design to apply to my situation?

Getting meta with these questions now:

  • How can I better summarize this problem and make a helpful title for the question with it?

  • Is this even the right place to ask? The question doesn't involve a single full line of code, so CodeReview is definitely not the right place, and StackOverflow doesn't seem right, either.

2

I would leave them completely separate. Services and servers (notice I carefully separate the abstracted hardware and the abstracted software) are separate things.

I would let each Service object have one Remote (or VMInstance or whatever you want to call it) property, possibly passed in a contructor, that could potentially be changeable (service migration...).

At that point, it may well be that you decide that while it's convenient to have an accessor for some property that (strictly speaking) probably depends solely on the Remote on your Service instance, you can implement it as a simple pass-through (at least for a get-type operation).

  • I thought about this, but it required me to have things like self.remote.ip everywhere. self.ip seems a lot better... and after all, whenever you're talking about the ip of a Service, you're really talking about the ip of the Server that is hosting it. The same goes for every other property on Server (or Remote or whatever name it has in the end.) – ArtOfWarfare Aug 6 '15 at 12:05
  • There's nothing stopping you from writing an "abstract service base class" that does this through-wiring for you, but whatever lets you express what you want, with the minimum maintenance hassle 2-3 years down the line. – Vatine Aug 8 '15 at 21:44
  • "it required me to have things like self.remote.ip everywhere" This would be a code smell. Why is anything outside of Remote needing to know the remotes IP. Avoid getters and setters like the plague, they are simply a sign that you don't know what your objects are actually supposed to being doing. Define classes around behaviour, not attributes. Objects are units of behaviour, not data groupings. If you think about your objects as units that do things, rather than units that hold data the design should be come clearer. martinfowler.com/bliki/AnemicDomainModel.html – Cormac Mulhall Aug 17 '15 at 14:46
0

It seems to me like "remote" is a property of an object, not something which defines it. In other words, have you thought about using composition rather than inheritance? For example, an AWSRemote is really a VM and a Remote, and a DB2Server could either be a specific VM or a physical machine, either locally or remotely. A DB2Server constructor could for example take an object which enables you to install and configure it, and another objects which lets you communicate with it as a user.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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