Extended Types and Global Lists

BlitzMax Forums/BlitzMax Beginners Area/Extended Types and Global Lists

Gabriel(Posted 2005) [#1]
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?


kyoryu(Posted 2005) [#2]
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.


Gabriel(Posted 2005) [#3]
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. :/


Cajun17(Posted 2005) [#4]
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.


PGF(Posted 2005) [#5]
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 ?


Gabriel(Posted 2005) [#6]
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.


Scott Shaver(Posted 2005) [#7]
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()



Gabriel(Posted 2005) [#8]
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.


Scott Shaver(Posted 2005) [#9]
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.


QuietBloke(Posted 2005) [#10]
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.


Gabriel(Posted 2005) [#11]
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.


TartanTangerine (was Indiepath)(Posted 2005) [#12]
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



Scott Shaver(Posted 2005) [#13]
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.


tonyg(Posted 2005) [#14]
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'


FlameDuck(Posted 2005) [#15]
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.


Gabriel(Posted 2005) [#16]
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.


FlameDuck(Posted 2005) [#17]
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>


Scott Shaver(Posted 2005) [#18]

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.


Gabriel(Posted 2005) [#19]
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.


tonyg(Posted 2005) [#20]
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.


Gabriel(Posted 2005) [#21]
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 :)


fredborg(Posted 2005) [#22]
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"



tonyg(Posted 2005) [#23]
Fredborg, that's very good.
How does it know which type each list object is without checking them?


Who was John Galt?(Posted 2005) [#24]
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).


Cajun17(Posted 2005) [#25]
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.


Gabriel(Posted 2005) [#26]
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.