An object-oriented way to separate a model from its presentation

Suppose we have an object representing the configuration of a piece of equipment. For argumentation - the temperature controller (TempController). It contains one property, a given temperature.

I need to save this configuration to a file for use on some other device. The file format (FormatA) is set to stone. I do not want the TempController object to know about the file format ... it just does not apply to this object. Therefore, I create another "FormatAExporter" object that converts the TempController to the desired result.

In a year, we are creating a new temperature controller, we will allow it to "AdvancedTempController", which has not only a preset value, but also has speed control, which means one or two more properties. A new file format has also been invented to preserve these properties ... let it be called FormatB.

Both file formats can represent both devices (suppose AdvancedTempController has reasonable defaults if it lacks settings).

So, here is the problem: not using "isa" or any other "fraudulent" way to find out what type of object I have, how does FormatBExporter handle both cases?

My first instinct is to have a method in every temperature controller that can provide a client exporter for this class, for example TempController.getExporter () and AdvancedTempController.getExporter (). It does not support multiple file formats.

The only other approach that comes to mind is to have a method in each temperature controller that returns a list of properties and their values, and then the formatter can decide how to output them. This will work, but it seems confusing.

UPDATE: With further work, this latter approach does not work very well. If all your types are simple, perhaps, but if your objects are objects, you end up just pushing the problem to the level ... you are forced to return a pair of String, Object values, and the exporter will need to know that the Objects should actually use them. Thus, it simply pushes the problem to a different level.

Are there any suggestions on how I can maintain this flexibility?

+6
design oop
source share
7 answers

What you can do is let TempControllers take responsibility for saving themselves using a common archiver.

class TempController { private Temperature _setPoint; public Temperature SetPoint { get; set;} public ImportFrom(Archive archive) { SetPoint = archive.Read("SetPoint"); } public ExportTo(Archive archive) { archive.Write("SetPoint", SetPoint); } } class AdvancedTempController { private Temperature _setPoint; private Rate _rateControl; public Temperature SetPoint { get; set;} public Rate RateControl { get; set;} public ImportFrom(Archive archive) { SetPoint = archive.Read("SetPoint"); RateControl = archive.ReadWithDefault("RateControl", Rate.Zero); } public ExportTo(Archive archive) { archive.Write("SetPoint", SetPoint); archive.Write("RateControl", RateControl); } } 

By storing it this way, the controllers don't care how the actual values ​​are saved, but you still keep the object's internals encapsulated.

Now you can define an abstract archive class that all archive classes can implement.

 abstract class Archive { public abstract object Read(string key); public abstract object ReadWithDefault(string key, object defaultValue); public abstract void Write(string key); } 

The FormatA archiver can do this in one way, and the FormatB archive can do it in another.

 class FormatAArchive : Archive { public object Read(string key) { // read stuff } public object ReadWithDefault(string key, object defaultValue) { // if store contains key, read stuff // else return default value } public void Write(string key) { // write stuff } } class FormatBArchive : Archive { public object Read(string key) { // read stuff } public object ReadWithDefault(string key, object defaultValue) { // if store contains key, read stuff // else return default value } public void Write(string key) { // write stuff } } 

You can add to another type of controller and pass it regardless of formatting. You can also create another formatter and pass it to any controller.

+4
source share

In C # or other languages ​​that support this, you can do this:

 class TempController { int SetPoint; } class AdvancedTempController : TempController { int Rate; } class FormatAExporter { void Export(TempController tc) { Write(tc.SetPoint); } } class FormatBExporter { void Export(TempController tc) { if (tc is AdvancedTempController) { Write((tc as AdvancedTempController).Rate); } Write(tc.SetPoint); } } 
+1
source share

I will have a "temp controller", through the getState method, to return the map (for example, in Python a dict, in a Javascript object, in C ++ - std :: map or std :: hashmap, etc. etc. ) about its properties and current values ​​- what did you think? It could not be simpler, it is completely expandable and completely separate from the use that it uses (display, serialization and c).

0
source share

If FormatBExporter accepts an AdvancedTempController, you can create a bridge class that makes the TempController compatible with the AdvancedTempController. You may need to add some getFormat () function to the AdvancedTempController.

For example:

 FormatBExporter exporterB; TempController tempController; AdvancedTempController bridged = TempToAdvancedTempBridge(tempController); exporterB.export(bridged); 

It is also possible to use a key-to-value mapping scheme. FormatAExporter exports / imports the value for the key "setpoint". FormatBExporter exports / imports values ​​for the keys "setpoint" and "ratecontrol". Thus, the old FormatAExporter can still read the new file format (it simply ignores the "ratecontrol"), and FormatBExporter can read the old file format (if the "ratecontrol" is absent, it uses the default value).

0
source share

Well, a lot depends on the file formats you are talking about.

If they are based on key / value combinations (including nested ones, for example, xml), then the presence of some kind of intermediate memory object that is freely typed and which can be thrown in the appropriate file format is a good way to do this.

If not, then you have a script in which you have four combinations of objects and file formats with custom logic for each script. In this case, it may not be possible to have a single view for each file format that can deal with any controller. In other words, if you cannot generalize a file format, you cannot generalize it.

I don’t really like the idea of ​​controllers having exporters - I’m just not a fan of objects that know about storage mechanisms and a lot (they can know about the storage concept and have a specific instance provided to them through some DI mechanism). But I think you agree with this for the same reasons.

0
source share

In the OO model, object methods as collective are the controller. It is more useful to split your program into M and V rather than C if you program with OO.

0
source share

I guess it will be a Factory method template to apply

0
source share

All Articles