Extended Types and Global Lists
BlitzMax Forums/BlitzMax Beginners Area/Extended Types and Global Lists
| ||
I'm extending a base class and I want each class to have it's own list of objects. But obviously there's not much point having a base class if I have to do everything in relation to the list separately for each type. Quick example code Type A Global List:TList Method New() If List=Null List=New TList End If ListAddLast List,Self End Method End Type Type B Extends A End Type Type C Extends A End Type Global AB=New B Global BB=New B Global CB=New B Global AC=New C Global BC=New C Global CC=New C Now if I try to examine the list from any of the types, it's all the same list, with 6 objects, 3 of each. What I'd like is for C to have a list of three C's and B to have a list of 3 B's. But I still want to be able to operate with the list in the base class. Hope that makes sense. I'll be having an awful lot of extended types and it will be a huge amount of extra code if I can't extend a base type. They all have so much in common that it really ought to be a huge saving, if I could just get lists behaving as I want. Suggestions? |
| ||
You could create separate lists in each type, and add them in the New method, but that would put all of your Cs in the B list as well. Best thing is probably to create 'Create' functions that populate the various lists correctly. You will need to create a list for each class though. |
| ||
If I can't "extend" the list and the create function, then there's really no code left to share. Practically all the shared code relies on the lists in some manner. Essentially I am created a large number of "databases". It seemed to me that the most effective way to do this was to have a base class called database which has methods/functions for loading, saving, searching, etc and then extend it down to the individual types. Then all I have to have in each type is the code specific to loading/saving one record. The overall loading/saving can be done in the base class. Isn't this the point of OO coding, and inheritance in particular? To save having identical code in lots of different places. Granted I'm very new to OO programming and I may be overlooking something very basic, but it's going to be very, very inefficient to have all of the code in every single database type. :/ |
| ||
Create an array of lists. Then have a field in each type to store the index that the list of that type is in the array. Either that or create a new type of container object, because a list is simply too simple to do what you want. OO has nothing to do with it. Even in C you'd have the same problem. |
| ||
You could have Load/Save methods in the derived types that override those in the base class. The base class list handling is properly shared but when it comes to loading or saving an instance the override methods are called. It sounds to me like you should rethink the overall design. I don't see any reason for the individual types to extend Database. Perhaps they should be types derived from a base Row type ? Here is a rough outline of how a database structure could be implemented: - There is a type T_Database that holds everything together and provides initialisation, finalisation and perhaps if required CreateTable, DropTable, statistics etc. It also provides things like the file or directory path and a collection (say a list or array) of T_Table objects - T_Table contains a collection of T_Column types that define the fields and a collection of T_Row types that store the data in this format. T_Column is only really required if you want to allow for the database to understand the row structure. This is important if you want to implement things like Select with a Where clause or indexes on certain columns. - T_Row is the base for the types that will actually be stored and manipulated. From it are derived say T_Enemy and T_Planet which are the types you want to handle. The derived types know which T_Table they are related to and they implement override methods to construct themself from (Load) or store themself to (Save) a string, stream, bank or whatever is your physical storage mechanism. - T_Row contains a lot of the code in methods like Insert, Update, Delete, Select. These are generic and rely on the derived Load/Save methods to operate on the specific type instances. Often the function/method parameters and return values will be variables or arrays of type T_Row. In your calling program of course they will be of the derived types T_Enemy etc which are compatible since they are derived from T_Row. This can get as sophisticated as you want. Even if you are starting with the simple stuff, think about what the objects actually are and what actions they perform. - A database is a collection of tables. Databases can be created and dropped. - A table is a collection of columns that define the fields and a collection of rows that contain the data in that format. Tables can be created and dropped. - A row contains the fields that make up a single entity. Rows can be inserted, updated, deleted or selected Perhaps somebody has already made either a BlitzMax interface to a proper DBMS or a DBMS like structure that would meet your requirements ? |
| ||
Create an array of lists. Then have a field in each type to store the index that the list of that type is in the array. Either that or create a new type of container object, because a list is simply too simple to do what you want. I can see how an array of lists helps, but if the list index has to be a field instead of a global, that makes it impossible to create, load, import or anything else where a type instance does not already exist. OO has nothing to do with it. Even in C you'd have the same problem. I'll take your word for that, but I find it hard to believe. This seems to me like a fundamental requirement if inheritance is to be of any use. This is only my second BMax project and I've really needed it in both. I managed to get by in the last project because I was dealing with small numbers of objects, but dealing with tens or hundreds of thousands of database records, I seriously don't want to slow things down to a crawl trawling through the wrong type of object. PGF: That's ten times more complicated than I will ever need. I could justify my design but you'll just have to take my word for it that the design the way I have it is perfect apart from the list issue and not be able to "extend" it. |
| ||
This isn't the greatest solution to the problem but it works. Cajun17 is right this isn't an oop issue. You are trying to do is override the behavior of the Global TList in the base type which isn't how oop works. You would need a new container type. Another possiblity would be to have a function in each dervied type call GetList which would create and return a new TList with only those objects in the global list that are of the specified type. This could get slow though and would create a lot of garbage.Type Base Global AllList:TList=Null Global ASubList:TList=Null Global BSubList:TList=Null Global CSubList:TList=Null Method New() If AllList=Null Then AllList=New TList If ASubList=Null Then ASubList=New TList If BSubList=Null Then BSubList=New TList If CSubList=Null Then CSubList=New TList ListAddLast AllList,Self End Method Method AddToSub(list:TList,o:Base) ListAddLast(list,o) End Method Method PrintList() If C(CSubList.first()) Then Print "C " + CSubList.count() ElseIf B(BSubList.first()) Then Print "B " + BSubList.count() ElseIf A(ASubList.first()) Then Print "A " + ASubList.count() EndIf Print "Base " + AllList.count() End Method End Type Type A Extends Base Function Create:A() Local o:A = New A o.AddToSub(ASubList,o) Return o End Function End Type Type B Extends A Function Create:B() Local o:B = New B o.AddToSub(BSubList,o) Return o End Function End Type Type C Extends A Function Create:C() Local o:C = New C o.AddToSub(CSubList,o) Return o End Function End Type Global aA:Base=A.Create() Global aB:Base=A.Create() Global aC:Base=A.Create() Global aD:Base=B.Create() Global aE:Base=B.Create() Global aF:Base=B.Create() Global aG:Base=C.Create() Global aH:Base=C.Create() Global aI:Base=C.Create() aA.PrintList() aD.PrintList() aG.PrintList() |
| ||
I didn't say that OOP works the way I'm trying to use it. Obviously it doesn't or I wouldn't be asking the correct way to do it. I said it seems insane if there isn't a different way of doing what I'm trying to do. The problem with your "solution" is you're still doing all the creating in the derived type, when the whole basis of my question is how to do it in the base type. There's no point in me having a base class if all the shared code has to go in the derived classes. I will just end up with a completely empty base class, because there's nothing left to share. |
| ||
Well I guess you just answered you own question then. If there is nothing different between the base and derived types other than the list handling you don't need derived types. |
| ||
I dont think it is a OOP issue. In my case I have a Sprite type with a global list of all sprites created. The New() method adds itself to the list. This means any new types I create that derive from the Sprite will also automatically add themselves to the List. That is what it is supposed to do. All the derived types are still basically a sprite and therefore do everything a sprite type does. What your asking for is that each derived type share all the common code but use a unique global field. I dont think any OO language will do what you want. In C++ I think you could achieve what you want using Templates. My C++ is not very hot but from what I understand you would write your common code as a template. Then each of the classes you want can be created from the template. The compiler would insert the common code for you. In effect each of the classes would be copies of the template. Equivalent to you cut/pasting your base code into each of your required types. The alternative is to redesign your code to work differently. for example : How about if you make the TList local in your base type. The top level database type will use the TList to store a list of tables. Each table type stores a List of Columns. So whenever you create a Table the table will have it's own list in which to store the columns. Just a thought. |
| ||
No, I said there's nothing that doesn't INVOLVE list handling. List handling, unsurprisingly is a major part of a "database" and there's a ton of code which involves the list in some shape or form. Having to have twenty copies of each function or method, one for each derived type seems insane. I might as well not bother with an OO language at all. I could go back to B3d. |
| ||
I've probably got completely the wrong end of the stick. This code will list the Base Class and the extended classes. Type A Global List:TList Field Test:Byte Method New() If List=Null List=New TList End If ListAddLast List,Self self.test = Rand(0,255) End Method End Type Type B Extends A End Type Type C Extends A End Type Global AB=New B Global BB=New B Global CB=New B Global AC=New C Global BC=New C Global CC=New C ' List all types For allTypes:A = EachIn a.List Print allTypes.Test Next Print "--------" ' List all B types For bTypes:B = EachIn a.List Print bTypes.Test Next Print "--------" ' List all C types For cTypes:C = EachIn a.List Print cTypes.Test Next |
| ||
well what are the derived types? I mean what purpose do they serve? What is the base type representing? I think we need more information about what you are trying to accomplish, not just the list issue. Sounds to me like you may not be modeling the problem correctly, hard to tell without know the entire scope of the problem you are trying to solve. ... Edit: I just reread one of your previous posts. You are modeling the problem incorrectly. Do something like QuiteBloke suggested. Have a Database class that keeps a list of Tables. Each table keeps a list of fields. Each field has a data type and its value. You don't need a new type for each database. |
| ||
IndiePath, I came up with the same thing but, if you put timing code in and create a lot of instances, the time taken is the same for each 'eachin' (i.e. it checks every instance on the list despite it's type). Type A Global List:TList Method New() If List=Null List=New TList End If ListAddLast List,Self End Method End Type Type B Extends A End Type Type C Extends A End Type For my_count = 1 To 50000 AB=New B Next For my_count = 1 To 10 AC=New c Next a_start=MilliSecs() For t:a = EachIn a.list a_count:+1 Next Print a_count a_end=MilliSecs() Print a_end-a_start b_start=MilliSecs() For u:b = EachIn a.list b_count:+1 Next Print b_count b_end=MilliSecs() Print b_end-b_start c_start=MilliSecs() For v:c = EachIn a.list c_count:+1 Next Print c_count c_end=MilliSecs() Print c_end - c_start Not to bad with 10nnn but 100nnn wastes a lot of cycles for type 'C' |
| ||
Obviously it doesn't or I wouldn't be asking the correct way to do it. I said it seems insane if there isn't a different way of doing what I'm trying to do. The "correct" way to do this, is to apply the Composite Design Pattern. The problem with your "solution" is you're still doing all the creating in the derived type, when the whole basis of my question is how to do it in the base type. You can't really do it like that.Whenever you're stuck with a creation problem, there's bound to be a Factory pattern that solves it. There's no point in me having a base class if all the shared code has to go in the derived classes. Additionally, there is no point in having a base class if the only thing it has in common with it's children is it's attributes.I know most people here consider OOP (never mind Design Patterns) a complete waste of time. Never the less it does save time, because the problem you have in your program is not unique, someone else will have run accross it before, and figured out a solution. |
| ||
Tim: Yes, that works and it's how I did it in my previous project, but it's still going through every instance, including all the unnecessary ones. It's not a problem with a few dozen instances, but it is with a few hundred thousand. EDIT: Oops, TonyG already beat me to that observation. I dont think it is a OOP issue. In my case I have a Sprite type with a global list of all sprites created. The New() method adds itself to the list. This means any new types I create that derive from the Sprite will also automatically add themselves to the List. That is what it is supposed to do. I covered this once already. I know it's not supposed to do that. I illustrated it like that because that's what I'd like it to do, and I'd like to know the easiest way of doing that. well what are the derived types? I mean what purpose do they serve? What is the base type representing? I think we need more information about what you are trying to accomplish, not just the list issue. Only if you're trying to answer a different question than the one I asked. I don't want to do the whole thing in a completely different way, or I would have done. If the answer to "how do I do this?" is "I don't know" or "You can't" that's perfectly fine. I'll find my own way. for example : How about if you make the TList local in your base type. The top level database type will use the TList to store a list of tables. Each table type stores a List of Columns. So whenever you create a Table the table will have it's own list in which to store the columns. Just a thought. Sounds like it would work, but as I said to the previous person to suggest it, it's far more complex than I really need and with some of the other things I'm doing, it's not going to be any faster than just having one list of everthing. I just reread one of your previous posts. You are modeling the problem incorrectly. No, modelling the program in a way I don't like is the wrong way to do it. This is a long term, large project and I finish projects because I write them in a way I'm comfortable with. You don't need a new type for each database. Yes I do. You just don't know why I do, because I haven't ( and won't ) go into every tiny detail of a huge project. You don't need to know it unless you're trying to tell me do to everything in a completely different manner, which I'm not going to do anyway. I can kludge my own workaround if there's no ideal way to do it. The "correct" way to do this, is to apply the Composite Design Pattern. You can't really do it like that.Whenever you're stuck with a creation problem, there's bound to be a Factory pattern that solves it. I have no idea what either of those are, but it sounds like a good jumping off point to go researching and reading. Thanks. I know most people here consider OOP (never mind Design Patterns) a complete waste of time. Never the less it does save time, because the problem you have in your program is not unique, someone else will have run accross it before, and figured out a solution. Very good point. |
| ||
I have no idea what either of those are, but it sounds like a good jumping off point to go researching and reading. Thanks. Yeah. Sorry I couldn't provide you with working code, but I haven't had much time for BlitzMAX recently due to other obligations. Besides which I'm sure other people are better at explaining them than me.The notorious GoF Book is a good place to start, if you're so inclined. It's a classic. :o> |
| ||
Yes I do. You just don't know why I do, because I haven't ( and won't ) go into every tiny detail of a huge project. You don't need to know it unless you're trying to tell me do to everything in a completely different manner, which I'm not going to do anyway. I can kludge my own workaround if there's no ideal way to do it. Forgive me, this may sound a bit rude, but... If you won't explain enough for us to help you and you refuse to listen to the help you are being giving then why did you bother asking the question? It's a bit crappy to ask for help and then yell at eveyone that trys to help you and say they are wrong. |
| ||
Yeah. Sorry I couldn't provide you with working code, but I haven't had much time for BlitzMAX recently due to other obligations. Besides which I'm sure other people are better at explaining them than me. Not at all, a point in the right direction is all I need. The notorious GoF Book is a good place to start, if you're so inclined. It's a classic. :o> The description makes it sound like a fascinating read. I've never looked into design patterns before, but it sounds like it could be something of a revelation if I can get into it. If you won't explain enough for us to help you You have enough information to help me. You only want more information because you're intent on having me do the project your way. I only do projects someone else's way if they're paying me. If I've misunderstood, I'll let you have my PayPal details and once the payment clears I'll do it just to your requirements. Failing that, I'll do it my way, thanks. and you refuse to listen to the help you are being giving I haven't refused to listen to help. Several suggestions simply don't answer the question I asked. The only help I've seen offered so far which attempted to answer the actual question as it was asked is Tim and TonyG ( and TonyG himself explained why that is just masking the problem ) and FlameDuck, whose advice I think sounds great. If there's an unwritten rule around here which says I have to take any and all advice offered, regardless of whether it helps or hinders me, I'm not aware of it. I've read all the replies, considered all the replies and picked the one which I think is most helpful. I appreciate the time everyone took to respond, and I believe I was quite courteous to everyone the first time. When it comes to coming back a second and third time to tell me you're right and I'm wrong, you've strayed from helpfulness into arrogance and I'm inclined to be a bit tetchy. I didn't, however, return the favour and say you were wrong, I simply indicated that it wasn't the right answer for me, so that I didn't waste more of your time preaching to me when I had made my decision. then why did you bother asking the question? It happened in the early hours in a brief moment of weakness when I forgot how the answer to so many questions on programming forums is like the old Irish joke. Q: "Can you tell me how to get to Kerry?" A: "Well sir, I wouldn't start from here if I was you." It's a bit crappy to ask for help and then yell at eveyone that trys to help you and say they are wrong. I haven't yelled at anyone. Aside from being impossible to do in type, I didn't even raise my caps lock at you. And it was you arrogantly suggesting my design was wrong, not the other way around. I have every confidence that your design suggestion is perfectly good, I just happen to know the way I work best. It's programming in a manner I'm comfortable with which has enabled me to finish as many projects as I have. Forgive me, this may sound a bit rude, but... You're right, it did. But since you asked nicely, I'll forgive you. |
| ||
Another shot in the dark. Could you have a next_same_type and prev_same_type field which holds the object pointer of the next and previous instance of the same type in the list? Rather than loop with eachin, run through the next_same_pointer values. Basically, you'd be simulating the list functions but only for instances of the same type. You could store these values in global variables within the type. |
| ||
Hmm.. I'm sure the OOP purists will decry it as evil or at the very least inelegant, but that's a pretty good solution, Tony. If I did things that way, the only speed hit would be storing all the extra pointers when I create and load, which happens very, very rarely. Going through the virtual lists should be practically as fast as going through separate lists. Yup, I like that. I think I'm still gonna buy FlameDuck's book suggestion, for future reference and self-improvement, but I think that solves my problem pretty well. Thanks :) |
| ||
I'd do it like this:Type A Global List:TList Field Test:Byte Field letter:String Method New() If List=Null Then List = New TList List.AddLast Self self.test = Rand(0,255) self.letter = "A" End Method End Type Type B Extends A Global List:TList Method New() If List=Null Then List = New TList List.AddLast Self self.letter = "B" EndMethod End Type Type C Extends A Global List:TList Method New() If List=Null Then List = New TList List.AddLast Self self.letter = "C" EndMethod End Type Global AB=New B Global BB=New B Global CB=New B Global AC=New C Global BC=New C Global CC=New C ' List all types Print "A,B, and C" For allTypes:A = EachIn a.List Print allTypes.letter+","+allTypes.Test Next Print "~n" ' List all B types Print "Only Bs" For bTypes:B = EachIn b.List Print bTypes.letter+","+bTypes.Test Next Print "~n" ' List all C types Print "Only Cs" For cTypes:B = EachIn c.List Print cTypes.letter+","+cTypes.Test Next Print "~n" |
| ||
Fredborg, that's very good. How does it know which type each list object is without checking them? |
| ||
Type parent Global ncreated=0 Field num Method New() DebugLog "parent new" Self.num=ncreated ncreated:+1 End Method Method addtolist(list:TList Var) If list=Null DebugLog "new list" list:TList=New TList End If ListAddLast list,Self End Method End Type Type a Extends parent Global list:TList Method New() DebugLog "a new" addtolist(list) End Method End Type Type b Extends parent Global list:TList Method New() DebugLog "b new" addtolist(list) End Method End Type Global a1:a=New a Global a2:a=New a Global a3:a=New a Global b1:b=New b Global b2:b=New b Global b3:b=New b DebugLog "a list" For ap:a=EachIn a.list DebugLog ap.num Next DebugLog "b list" For bp:b=EachIn b.list DebugLog bp.num Next Wouldn't have bothered if I had seen FredBorg's first, but here it is, with a slightly different twist. You can see each child type has its own global list (within that type). |
| ||
Really what you need in this situation is an Adjacency List:Const TYPE_COUNT = 6 Const TYPE_A = 0 Const TYPE_B = 1 Const TYPE_C = 2 Const TYPE_D = 3 Const TYPE_E = 4 Const TYPE_F = 5 Type AdjList Field loopStart:Int Field theLists:AdjNode[] Method New() theLists = New AdjNode[TYPE_COUNT] For Local i:Int = 0 To TYPE_COUNT-1 theLists[i] = New AdjNode Next EndMethod Method addRelationShip(p:Int, c:Int) theLists[p].addChild(c) EndMethod Method addObject(o:AdjType) theLists[o._type].addObject(o) If o.p_Type = -1 Then Return EndMethod Method removeObject(o:AdjType) theLists[o._type].removeObject(o) EndMethod Method onlyThisType:Object[]() Return theLists[loopStart].aList EndMethod Method setLoopStart(i:Int) loopStart = i End Method Method ObjectEnumerator:AdjIterator() Local temp:AdjIterator = New AdjIterator temp.theLists = theLists temp.n_Index = loopStart temp.Init() Return temp EndMethod EndType Type AdjNode Field children:Int[] Field aList:AdjType[] Field num:Int Method addObject(o:AdjType) If num=aList.length Then grow() aList[num]=o o.o_Index = num num:+1 End Method Method removeObject(o:AdjType) If o.o_Index > aList.length-1 Then Return num:-1 aList[num].o_Index = o.o_Index aList[o.o_Index] = aList[num] o.o_Index = -1 EndMethod Method addChild(i:Int) children = children[..children.length+1] children[children.length-1] = i EndMethod Method grow() aList = aList[..aList.length+10] EndMethod EndType Type AdjIterator Field n_Index:Int Field o_Index:Int Field theLists:AdjNode[] Field que:intQue Method New() que = New intQue EndMethod Method Init() For Local i:Int = 0 To theLists[n_Index].children.length-1 que.push(theLists[n_Index].children[i]) Next EndMethod Method HasNext:Int() If o_Index < theLists[n_Index].aList.length Then Return True Repeat If Not que.isEmpty() Then n_Index = que.pop() o_Index = 0 For Local i:Int = 0 To theLists[n_Index].children.length-1 que.push(theLists[n_Index].children[i]) Next If theLists[n_Index].aList.length > o_Index Then Return True Else Return False End If Forever EndMethod Method NextObject:Object() o_Index:+1 Return theLists[n_Index].aList[o_Index-1] EndMethod EndType Type AdjType Field _Type:Int Field p_Type:Int Field o_Index:Int Method Init() Abstract Function ForList:Object() Abstract End Type Type intQue Field que:Int[] Field front:Int Field back:Int Field num:Int Method New() que = que[..5] EndMethod Method push(i:Int) If isFull() Then Return que[back]=i back:+1 back:*(back < que.length) EndMethod Method pop:Int() If isEmpty() Then Return If front = que.length-1 Then front = 0 Return que[que.length-1] Else front:+1 Return que[front-1] End If End Method Method isEmpty:Int() If (front = back) And (num = 0) Then shrink() Return True End If Return False EndMethod Method isFull:Int() If (front = back) And (num <> 0) Then grow() Return False EndMethod Method grow() que = que[..que.length+5] EndMethod Method shrink() que = que[..5] EndMethod EndType Type A Extends AdjType Global List:AdjList Method Init() _Type=TYPE_A p_Type=-1 List.addObject(Self) EndMethod Function ForList:AdjList() List.setLoopStart(TYPE_A) Return List EndFunction Method setupRelation() If p_Type > -1 Then List.addRelationship(p_Type,_Type) EndMethod EndType Type B Extends A Method Init() _Type=TYPE_B p_Type=TYPE_A List.addObject(Self) EndMethod Function ForList:AdjList() List.setLoopStart(TYPE_B) Return List EndFunction EndType Type C Extends A Method Init() _Type=TYPE_C p_Type=TYPE_A List.addObject(Self) EndMethod Function ForList:AdjList() List.setLoopStart(TYPE_C) Return List EndFunction End Type Type D Extends C Method Init() _Type=TYPE_D p_Type=TYPE_C List.addObject(Self) EndMethod Function ForList:AdjList() List.setLoopStart(TYPE_D) Return List EndFunction End Type Type E Extends C Method Init() _Type=TYPE_E p_Type=TYPE_C List.addObject(Self) EndMethod Function ForList:AdjList() List.setLoopStart(TYPE_E) Return List EndFunction End Type Type F Extends E Method Init() _Type=TYPE_F p_Type=TYPE_E List.addObject(Self) EndMethod Function ForList:AdjList() List.setLoopStart(TYPE_F) Return List EndFunction End Type A.List = New AdjList Local aa:A = New A Local bb:B = New B Local cc:C = New C Local dd:D = New D Local ee:E = New E Local ff:F = New F aa.Init() aa.setupRelation() bb.Init() bb.setupRelation() cc.Init() cc.setupRelation() dd.Init() dd.setupRelation() ee.Init() ee.setupRelation() ff.Init() ff.setupRelation() aa = New A bb = New B cc = New C dd = New D ee = New E ff = New F aa.Init() bb.Init() cc.Init() dd.Init() ee.Init() ff.Init() Print "---A---" For aa = EachIn A.ForList() Print aa._Type Next Print Print "---B---" For bb = EachIn B.ForList() Print bb._Type Next Print Print "---C---" For cc = EachIn C.ForList() Print cc._Type Next Print Print "---A---" For aa = EachIn A.ForList().onlyThisType() Print aa._Type Next Print Print "---D---" For dd = EachIn D.ForList() Print dd._Type Next Print Print "---E---" For ee = EachIn E.ForList() Print ee._Type Next Print Print "---F---" For ff = EachIn F.ForList() Print ff._Type Next Since there've already been solutions posted I won't document or explain it unless someone would like me to. Basicly it's a list of lists with a hierarchy. The base can see the objects that extend it. Also 20 extended types can be extended by 5 types etc... and will all be in one list. |
| ||
Thanks for the additional suggestions. I've decided to go with my own solution for the moment. Since none of the databases need to interrelate in the base class, I have a tiny function in each of the extended classes which copies list handles and anything else in the way of type globals into globals held in the base class. I call that function ActivateDB() and then just ADB.ActivateDB() and Database.Load() Database.Save() ..or whatever I'm sure it's inelegant and whatever else but it lets me keep things the way I want them and it works great so far. |