PÄSSION ~ Mixins and subclasses questions

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
Pliskin09
Citizen
Posts: 89
Joined: Fri Jul 24, 2009 8:30 am

PÄSSION ~ Mixins and subclasses questions

Post by Pliskin09 »

i've attached my "game". run it and check out stdout.txt to see the results (nothing "visual". it just output's some text).

a_dog's describe variable works well. i have a bit of a problem with checking to see if it can fly though. the mammal class has fly and canFly functions. canFly just returns 0, and fly prints "I've no wings, I can't fly!". i then include the hasWings module to the class and that 'overwrites' (?) the 2 functions with it's own ones (the new canFly returns 1 (true) and fly returns 'flap flap flap!'. i don't know if this is the right way to do it. the reason i have the 2 fly and canFly functions in the mammal class in the first place is because i have no other way of checking to see if it can fly or not.

secondly, what i tried to do was create a subclass for mammal called dog. dog can just be empty if i needed but it was there so that when i did local dog1 = dog:new(...) i knew i was getting a dog. i could then make other subclasses for mammal, like "bat" which would inclue "hasWings". the problem here is when i make an empty subclass "dog", and call the function describe, it doesnt exist. do i need to re-write all the functions in dog and call super(self,...)? that'd work, but it just seems like alot of re-writing for nothing.
User avatar
TechnoCat
Inner party member
Posts: 1611
Joined: Thu Jul 30, 2009 12:31 am
Location: Milwaukee, WI
Contact:

Re: PÄSSION ~ Mixins and subclasses questions

Post by TechnoCat »

Nothing is attached.
User avatar
Pliskin09
Citizen
Posts: 89
Joined: Fri Jul 24, 2009 8:30 am

Re: PÄSSION ~ Mixins and subclasses questions

Post by Pliskin09 »

sorry..
Attachments
middleclass.zip
(2.39 KiB) Downloaded 172 times
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: PÄSSION ~ Mixins and subclasses questions

Post by kikito »

Ok, here are some comments:

You don't need to store the class name on your instances like this:

Code: Select all

function X:initialize()
  self.name_type = name_type
end
...

print('I am a ' .. self.name_type .. '.')
Instead, you can use the class.name:

Code: Select all

function X:initialize()
end
print('I am a ' .. self.class.name .. '.') --this will print "I am a X."
In your case, it will print "I am a Mammal" - if you want to print "I am a Dog" you must have a Dog class - but you have the canFly() problem. Let's handle that.

The easiest way to test if a class implements a function is ... to see if that function is there!

I would change this:

Code: Select all

if self:canFly() then
  print('I can fly!')
  self:fly()
else
  print("I can't fly!");
end
To this:

Code: Select all

if type(self.fly) == "function" then
  print('I can fly!')
  self:fly()
else
  print("I can't fly!");
end
Note that you will not need 'canFly' any more - it is safe to remove it from your interface and Mammal. Your Dog class now only needs an initializer that calls super.

Code: Select all

Dog = Mammal:subclass('Dog')
function Dog:initialize(name,color,sound)
	super(self,name,color,sound)
end
This approach is similar to what is done in javascript libraries, for handling browser compatibility. Instead of asking "am I in IE or Firefox?" you just ask "do I have xxx function?". For your particular example, this is the easiest solution.

However, for a big interface with lots of functions it would be tiresome to test all them. In those cases you could use a solution similar to the one that you implemented. But instead of a function, you could use a boolean:

Code: Select all

hasWings = {}
hasWings.canFly=true --the boolean
function hasWings.fly()
  print('flap flap flap!')
end
... -- more functions on hasWings here
Then, in your code, you can do:

Code: Select all

if(self.canFly) then
  ... -- use lots of hasWings functions here
end
canFly will return true if the interface is implemented, and nil otherwise.

I hope this helps.

[EDIT - answered *both* questions instead of just the first]
When I write def I mean function.
User avatar
Pliskin09
Citizen
Posts: 89
Joined: Fri Jul 24, 2009 8:30 am

Re: PÄSSION ~ Mixins and subclasses questions

Post by Pliskin09 »

Thanks kikito! very help and thorough answer. i've fixed up my script. i've also attached it to this post for anyone who is at my level and wishes to learn from it

Code: Select all

require 'MiddleClass.lua'

Animal = class('Animal')
function Animal:initialize(name,color)
	self.name = name
	self.color = color
end
function Animal:describe()
	print('My name is ' .. self.name .. '.')
	print('I am of a ' .. self.color .. ' color.')
end

hasWings = {}
function hasWings.fly()
	print('flap flap flap!')
end
hasWings.canFly = true

Mammal = Animal:subclass('Mammal')

function Mammal:initialize(name,color,sound)
	super(self,name,color)
	self.sound = sound or nil
end
function Mammal:describe()
	super(self)
	print('I am a ' .. self.name_type .. '.')
	if self.sound ~= nil then
		print('I can speak!')
		self:speak(5)
	else
		print("I can't speak!")
	end
	
	if self.canFly == true then
		print('I can fly!');
		self:fly();
	else
		print("I can't fly!");
	end
end
	
function Mammal:speak(count)
	count = count or 1;
	if self.sound ~= nil then
		repeat
			print(self.sound .. '!!');
			count = count - 1
		until count == 0
	else
		print('...!!!');
	end
end

--make a dog
Dog = Mammal:subclass('Dog')
function Dog:initialize(name,color,sound)
	super(self,name,color,sound)
	self.name_type = self.class.name
end

local dog1 = Dog:new('Billy','white','wolf')
dog1:describe()

print('----')

--make a bat
Bat = Mammal:subclass('Bat')
function Bat:initialize(name,color,sound)
	super(self,name,color,sound)
	self.name_type = self.class.name
end
Bat:includes(hasWings)

local bat1 = Bat:new('John','brown')
bat1:describe()



Attachments
middleclass.love
(2.26 KiB) Downloaded 168 times
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: PÄSSION ~ Mixins and subclasses questions

Post by kikito »

Hmm your code works, but it has a weakness.

You are using self.name_type on Mammal. However, this member variable is being set by Dog and Bat, but not by Mammal. This is easy to control in your example, but is not a good practice in general; with more complicated classes you will end up forgetting to set up the variables on your subclasses.

So you should either request it as a parameter on your Mammal constructor, and set it up there, or better, just use class.name directly and ignore name_type. You would end up with something like this:

Code: Select all

require 'MiddleClass.lua'

Animal = class('Animal')
function Animal:initialize(name,color)
   self.name = name
   self.color = color
end
function Animal:describe()
   print('My name is ' .. self.name .. '.')
   print('I am of a ' .. self.color .. ' color.')
end

hasWings = {}
function hasWings.fly()
   print('flap flap flap!')
end
hasWings.canFly = true

Mammal = Animal:subclass('Mammal')

function Mammal:initialize(name,color,sound)
   super(self,name,color)
   self.sound = sound or nil
end
function Mammal:describe()
   super(self)
   print('I am a ' .. self.class.name .. '.')
   if self.sound ~= nil then
      print('I can speak!')
      self:speak(5)
   else
      print("I can't speak!")
   end
   
   if self.canFly == true then
      print('I can fly!');
      self:fly();
   else
      print("I can't fly!");
   end
end
   
function Mammal:speak(count)
   count = count or 1;
   if self.sound ~= nil then
      repeat
         print(self.sound .. '!!');
         count = count - 1
      until count == 0
   else
      print('...!!!');
   end
end

--make a dog
Dog = Mammal:subclass('Dog')
function Dog:initialize(name,color,sound)
   super(self,name,color,sound)
end

local dog1 = Dog:new('Billy','white','wolf')
dog1:describe()

print('----')

--make a bat
Bat = Mammal:subclass('Bat')
function Bat:initialize(name,color,sound)
   super(self,name,color,sound)
end
Bat:includes(hasWings)

local bat1 = Bat:new('John','brown')
bat1:describe()
When I write def I mean function.
Post Reply

Who is online

Users browsing this forum: No registered users and 3 guests