|
ms
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Inheritance problemThis problem is more difficult to explain than the ones in my previous posts. For this one, I just need some good design practice advice, from an intuitive point of view. I have three classes, named Plot1, Plot2 and Plot3. Plot2 derives from Plot1, and Plot3 derives from Plot2. I have two more classes, named Cell2 and Cell3. Cell3 derives from Cell2. So far, so good. Now, one of the member fields of Plot2 is an array of cells of type Cell2 (declaration is "public Cell2[,] cell;"). And one of the member fields of Plot3 is an array of cells of type Cell3 (declaration is "new public Cell3[,] cell;"). Graphically: Plot1 | Plot2 has member field of type Cell2| | | Plot3 has member field of type Cell3| | Suppose that the kind of plot that I need in my application is an instance of Plot3. I separated Plot3 from Plot2 (and Cell3 from Cell2) expecting to reuse, in Plot3, a lot of code present in Plot2. That is, most of the operations with cells need only the member fields present in Cell2, and those operations are done at level 2 (in Plot2 code). I wanted to reuse that code in level 3, and only add the new code (in Plot3) which is specific for member fiels of Cell3 _not_ present in Cell2. However, it looks like I can't reuse anything, because every time I invoke, from Plot3 (using base.<whatever>()), the code in Plot2 that refers to cells, it refers to instances of type Cell2, not Cell3. And I haven't allocated space for the Cell2 cells. Only for the Cell3 cells, of course. I mean, when I run my application, I don't want any "...=new Cell2[..,..]" being executed. Only "...=new Cell3[..,..]". Those cells take a lot of memory each, and the level2 ones would never be used anyway. So, how to make the code in Plot2 that does the "common" operations with cells, refer to the most-specific cells (which, when called from a Plot3 instance, would be Cell3 cells), instead of to its Cell2 cells? I mean, I need something like virtual/override, but for member fields. The compiler doesn't let me use those modifiers for member fiels. Anyone got the idea of my problem? I think this must be a relatively usual need. How to proceed in these cases? Thank you very much, Bill Do you need to be able to hold a reference somewhere in your app which is
either a Plot1, Plot2, or Plot3. e.g. public void DoSomething(Plot1 plot) { plot.Execute(); } where you could pass an instance of Plot1, Plot2, or Plot3? Also, the same for Cell2 and Cell3? It looks to me like you are inheriting for the purpose of code reuse. It's hard to help though with such an abstract description of the problem. On Fri, 19 Dec 2008 15:51:12 -0000, "Peter Morris"
<mrpmorrisNO@SPAMgmail.com> wrote: >Do you need to be able to hold a reference somewhere in your app which is No, I don't need that.>either a Plot1, Plot2, or Plot3. e.g. > > >public void DoSomething(Plot1 plot) >{ > plot.Execute(); >} > >where you could pass an instance of Plot1, Plot2, or Plot3? >Also, the same for Cell2 and Cell3? That's one of the main reasons to the existence of inheritance.> > >It looks to me like you are inheriting for the purpose of code reuse. Code reuse, and with a good partitioning of the problem. >It's Plot1 does not subdivide the area in cells. It just initializes>hard to help though with such an abstract description of the problem. Direct3D to be used on that large canvas. It has two abstract methods (DX_Resize() and BuildScene()) that must be implemented in a derived class. Plot2 derives from Plot1 and adds subdivision of the canvas in an array of mxn Cell2 cells, each one having axes, cursor and storage for one y=f(x) 2D plot. It provides the first implementations for DX_Resize() and BuildScene(), which basically recompute the cell sizes and draw, for each one of them, the axes and the y=f(x) curve, when needed. Plot2 does not populate the y=f(x) curves. It just draws them. Plot3A derives from Plot2, and adds code to populate the y=f(x) curves which is optimized for real time. The user of Plot3A wants to see the curves continuously scrolling towards the left, on each one of the cells. They don't want to do zooms, or measure times or amplitudes. Just have a quick image of the shape of the curve being shown. Thanks to the optimization for real time, I can draw hundreds of frames per second, at 1680x1050x32. Plot3A uses Cell3A cells, which derive from (extend) Cell2. Plot3B also derives from Plot2, and adds code to populate the y=f(x) curves but not to be shown in real time. Instead, to give the user the ability to zoom (especially with good anti-aliasing), measure times and amplitudes, move in time either in a synchronous way for all the cells or in an independent way, etc. Suppose that the end user needs to include one of my custom controls in a Form, to show several curves in real time. He/she will instantiate Plot3A, and will call, among some other things, its Init() method. Now, Plot3A.Init() should reuse what Plot1.Init() does (initialize Direc3D), should also ideally reuse what Plot2.Init() does (initializing the cells), and also add some new code, specific to Plot3A. I wrote "ideally" because I would like to reuse what Plot2.Init() does, but I will probably don't do it. Plot2.Init() executes "...=new Cell2[..,..]" at some point, and I don't need that new to be executed. I need a "...=new Cell3[..,..]" to be executed, so, I'll have to repeat that code in Plot3.Init(), but pointing to the Cell3 type variable. Remember that the user is instantiating Plot3A. I would like Plot2.Init() refer to Cell3 cells, but it can't (that I know). I have all this code altogether, without inheritance, and works neatly. The "altogether" equivalent of Plot3A draws 8x8 subplots in real time at more than 200 frames per second. My problem is not having the hierarchy Plot1 -> Plot2 -> Plot3 or the hierarchy Cell2 -> Cell3. It's having them linked to each other. Yes, I'm missing something, or I'm too sleepy, but the partitioning does make sense to me, and I don't see an elegant solution to implement it, without repeating much code from level to level. I don't know if that clarified what my problem is. Thanks.
Show quote
Hide quote
> Suppose that the end user needs to include one of my custom controls You'll need the newest C# which adds support for covariance in order to > in a Form, to show several curves in real time. He/she will > instantiate Plot3A, and will call, among some other things, its Init() > method. Now, Plot3A.Init() should reuse what Plot1.Init() does > (initialize Direc3D), should also ideally reuse what Plot2.Init() does > (initializing the cells), and also add some new code, specific to > Plot3A. I wrote "ideally" because I would like to reuse what > Plot2.Init() does, but I will probably don't do it. Plot2.Init() > executes "...=new Cell2[..,..]" at some point, and I don't need that > new to be executed. I need a "...=new Cell3[..,..]" to be executed, > so, I'll have to repeat that code in Plot3.Init(), but pointing to the > Cell3 type variable. Remember that the user is instantiating Plot3A. I > would like Plot2.Init() refer to Cell3 cells, but it can't (that I > know). > > I have all this code altogether, without inheritance, and works > neatly. The "altogether" equivalent of Plot3A draws 8x8 subplots in > real time at more than 200 frames per second. My problem is not having > the hierarchy Plot1 -> Plot2 -> Plot3 or the hierarchy Cell2 -> Cell3. > It's having them linked to each other. Yes, I'm missing something, or > I'm too sleepy, but the partitioning does make sense to me, and I > don't see an elegant solution to implement it, without repeating much > code from level to level. really do this properly, then something like: interface ILookup<in S, out T> { T operator[](S index); } Then Plot2 needs an abstract property of type ILookup<int, Cell2> which is implemented in Plot3a and Plot3b using collections of Cell3a and Cell3b respectively. OTOH, you can do it with any version of C# by adding an abstract Cell2 GetCell(int index) method to Plot2 and implementing that in Plot3a and Plot3b to use the actual arrays of Cell3a and Cell3b as needed. What you shouldn't do is use the broken covariance implemented on arrays, which will add runtime type checks everywhere and kill performance. If you don't want Plot2 to become abstract, you'll need Plot1Base and Plot2Base classes which are abstract, Plot3a extends Plot2Base but not Plot2. The part of the Init functions that create the arrays go in the leaf classes, the reusable parts into the PlotNBase classes. >>where you could pass an instance of Plot1, Plot2, or Plot3? Then you don't need inheritance.> > No, I don't need that. >>It looks to me like you are inheriting for the purpose of code reuse. It might be a good argument for using objects (a single object implements > > That's one of the main reasons to the existence of inheritance. certain functionality) but that doesn't mean you must descend from the object in order to use that functionality. > Code reuse, and with a good partitioning of the problem. That's objects, not inheritance.> Plot1 does not subdivide the area in cells. It just initializes Okay, *now* it looks like you need inheritance :-) You need slight > Direct3D to be used on that large canvas. It has two abstract methods > (DX_Resize() and BuildScene()) that must be implemented in a derived > class. variations in the implementation. I still find it hard to understand your model but I will give you a couple of ideas and hopefully you will see a fit for one (or both). 01: An abstract way to get a list of cells Problem: Plot2 has a property Cell2[] and Plot3[] has a property Cell3[], but you need a way to get a Cell[] property. Solution: Have an abstract method on Plot1 protected Cell[] GetCells(); override it in descendants, convert your Cell3[] or Cell2[] to Cell[] protected Cell[] GetCells() { List<Cell> cells = new List<Cell>(this.Cells); return cells.ConvertAll(value => (Cell)value).ToArray(); } 02: An abstract way of creating an instance of the correct cell kind Problem: The base class needs to create instances of Cell, but you don't know which class type to create instances of. Solution: Have a creation method on your Point class protected abstract Cell CreateCell(); and override it in your descendant Point3 protected override Cell CreateCell() { return new Cell3(); } //Code that uses it public void Cell AddCell() { return CreateCell(); } If neither of these are relevant then past a couple of lines that show what you are trying to execute, what result you are experiencing, and what result you desire. On Fri, 19 Dec 2008 20:28:38 -0000, "Peter Morris"
<mrpmorrisNO@SPAMgmail.com> wrote: >>>It looks to me like you are inheriting for the purpose of code reuse. I didn't say that. I never said that code reuse implies inheritance. I>> >> That's one of the main reasons to the existence of inheritance. > >It might be a good argument for using objects (a single object implements >certain functionality) but that doesn't mean you must descend from the >object in order to use that functionality. said that inheritance provides code reuse, among other things. >> Code reuse, and with a good partitioning of the problem. Partitioning does not necessarily mean horizontal partitioning. It may> >That's objects, not inheritance. mean vertical partitioning, which was my case. Come on :-) I couldn't have written what I wrote if I didn't know the difference between objects and inheritance. >I still find it hard to understand your model but I will give you a couple Your two ideas were good. I did use your second one, and could make>of ideas and hopefully you will see a fit for one (or both). >01: An abstract way to get a list of cells >02: An abstract way of creating an instance of the correct cell kind the partitioned model work. However, a new problem (now with indexers) forces me to do not very elegant casts at the application level (the one that instantiates Plot3A, for instance). I'll try to solve it, but if I can't, I'll paste some code. Thanks a lot for your time.
Other interesting topics
Inheritance - Accessing two levels above mine - Now with virtual+override
About generic List<string> Finding a specific element using LINQ XML => Object stored in memory? Help with RegEx how to use count on a collection Inheritance - Accessing two levels above mine Data Objects vs fields relation between cryptoAPI generated machinekeystore and the key p Web Service Problem |
|||||||||||||||||||||||