Reputation: 1982
(I am using the gosu gem to draw to the screen.)
I am working on a file tree in ruby (not rails). Right now I have something like this:
class FileTree
@files = []
end
class Folder
@assets = []
def left_click
# Open or close
end
end
file_tree = FileTree.new
folder1 = Folder.new
folder2 = Folder.new
file_tree.files << folder1 << folder2
Now the folders get drawn to the screen, and the user should be able to click them to open/close them, and view the assets that are inside. I am not sure the best way to handle this change of state.
My first thought was to have a bool @opened
, and just check that to determine if assets are @visible
(another bool). However, I would like to replace the conditionals with polymorphism.
So I made ClosedFolder
and OpenFolder
classes that inherit from Folder, and then just initialized ClosedFolder
s into my FileTree
. However, now I need to click the folder and have it change to an OpenFolder
- but the file_tree doesn't know about the changed object and I have basically had to do a weird swap to get it in place.
I then tried adding @type
to Folder
, making modules of ClosedFolder and OpenFolder, and then making methods that do this:
class Folder
include ClosedFolder, OpenFolder
def left_click
public_send("#{@type}_left_click")
end
end
module ClosedFolder
def closed_folder_left_click
@type = :open_folder
@assets = get_files
end
end
module OpenFolder
def open_folder_left_click
@type = :closed_folder
@assets = []
end
end
When I ran this code, my CPU shot through the roof. I also don't like naming every method after the module/type.
So, how do I handle this problem? Are conditional checks the way to go? Would love to be able to just call left_click
on my asset and have it use the method in the correct object.
Upvotes: 1
Views: 212
Reputation: 1982
After some more thought, this just wants to be a state_machine, which is nearly like a case or branching ifs. However, we were able to implement a way to handle this without conditionals that feels pretty good:
class Folder
def initialize(name)
@name = name
@state = ClosedFolderState.new(self)
end
attr_reader :name
attr_accessor :state
def pretty_name
"#{@state.icon} #{@name}"
end
def left_click
@state.left_click
end
end
class ClosedFolderState
def initialize(folder)
@folder = folder
end
def icon
'+'
end
def left_click
@folder.state = OpenFolderState.new(@folder)
end
end
class OpenFolderState
def initialize(folder)
@folder = folder
end
def icon
'-'
end
def left_click
@folder.state = ClosedFolderState.new(@folder)
end
end
This may be overkill, but this seems to meet our goal of just seeing if we can move away from conditionals, and here using a decorator seems to work. I am not saying this would be everyone's preferred solution, but I think this answers my question.
Upvotes: 1
Reputation: 36860
Just seems like a very complicated way to avoid some simple conditionals.
If it were my project I'd be going down this route...
class Folder
def initialize
@assets = []
@open = false
end
def left_click
@open = !@open
@assets = @open ? get_files : []
end
end
Upvotes: 1