iCalendar library (critique new use cases for qCal library)
Moderator: General Moderators
Alright I need some design advice. I decided to go with arborint's advice to seperate out io / rendering / etc. into their own classes. This means to render a standard icalendar-formatted calendar, I do something like:
That seems inconvenient to me. I mean, that would be fine if you were using a custom renderer (for hCal or xCal or whatever else you want to render it as), but most of the time you'll just be using the standard icalendar renderer. It would be more convenient (IMO) if you could just rely on the calendar object's render() method to know how to render. So if you haven't told it to do any different, it would just render w/default renderer (icalendar)
If you need to render it with a "special" renderer, you'd do this:
Or maybe even...
What do you guys think?
Code: Select all
$cal = qCal::create();
// do stuff with calendar .. snip ..
$renderer = new qCal_Renderer_icalendar;
$renderer->render($cal)Code: Select all
$cal = qCal::create();
// since it hasn't been told any different, it just renders w/default renderer (to icalendar format)
$cal->render();Code: Select all
$cal = qCal::create();
$cal->setRenderer(new qCal_Renderer_hCal);
$cal->render();Code: Select all
$cal = qCal::create();
$cal->render(new qCal_Renderer_hCal);- RobertGonzalez
- Site Administrator
- Posts: 14293
- Joined: Tue Sep 09, 2003 6:04 pm
- Location: Fremont, CA, USA
Any advantage of setRenderer over simply:
Thanks Everah! 
Code: Select all
$cal = qCal::create();
$cal->render(new qCal_Renderer_hCal);- RobertGonzalez
- Site Administrator
- Posts: 14293
- Joined: Tue Sep 09, 2003 6:04 pm
- Location: Fremont, CA, USA
Render is typically a final step. It might not make much sense, but what if you wanted to render something in one way, then immediately render it in another (like I said, not much sense, but bear with me).
Using something a la setRenderer would give you the ability to set a renderer without rendering. So you could feasibly change the rendering engine midstream, if you wanted to, with the need for actual output.
Does that make any sense?
Using something a la setRenderer would give you the ability to set a renderer without rendering. So you could feasibly change the rendering engine midstream, if you wanted to, with the need for actual output.
Does that make any sense?
- John Cartwright
- Site Admin
- Posts: 11470
- Joined: Tue Dec 23, 2003 2:10 am
- Location: Toronto
- Contact:
I would probably stick to this method, simply because it would be easier to test than having to test two seperate methods. They essentially are doing the exact same thing, but this does it in fewer steps, with a simpler interface to the object.The Ninja Space Goat wrote:Any advantage of setRenderer over simply:
Thanks Everah!Code: Select all
$cal = qCal::create(); $cal->render(new qCal_Renderer_hCal);
If this is the case, I would probably have a setRender(), but you could easily accomplish this anyways with just a render method anyways.Using something a la setRenderer would give you the ability to set a renderer without rendering. So you could feasibly change the rendering engine midstream, if you wanted to, with the need for actual output.
Alright, I'm having some trouble here again. I'm hoping somebody can just give me a push in the right direction. I've tried several things, but none seem flexible enough. I've got qCal_Component and qCal_Property. Components can have other components, as well as properties as children. My renderer needs to be able to traverse this tree of qCal_Components / qCal_Properties and render them based on logic provided by subclasses of qCal_Renderer. Does that make sense?
I have a composite pattern set up for the component / property tree and that is working out just fine. The issue comes in when I try and give flexibility to classes that extend qCal_Renderer (this way I could have qCal_Renderer_hCal, qCal_Renderer_xCal, etc.). I'm just having a difficult time figuring out where to put what logic. Here is what I've been trying:
To render the data as standard icalendar format is dead easy, but I would like for it to also be easy to render the data in xhtml, xml, json, whatever. This just doesn't feel like it will provide that kind of flexibility, and also I end up looping through component's children twice here... which feels wrong. DRY?
I have a composite pattern set up for the component / property tree and that is working out just fine. The issue comes in when I try and give flexibility to classes that extend qCal_Renderer (this way I could have qCal_Renderer_hCal, qCal_Renderer_xCal, etc.). I'm just having a difficult time figuring out where to put what logic. Here is what I've been trying:
Code: Select all
abstract class qCal_Renderer
{
public function render(qCal_Component $component)
{
$ret = '';
foreach ($component->getChildren() as $child)
{
if ($child instanceof qCal_Component) $ret .= $this->renderComponent($child);
elseif ($child instanceof qCal_Property) $ret .= $this->renderProperty($child);
}
}
abstract protected function renderComponent(qCal_Component $component);
abstract protected function renderProperty(qCal_Property $component);
}
class qCal_Renderer_icalendar
{
protected function renderComponent(qCal_Component $component)
{
foreach ($component->getChildren() as $child)
{
// render each child
}
}
protected function renderProperty(qCal_Property $component)
{
// render property
}
}- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
I haven't had a chance to look and think about your code (hopefully I will get some time), but the above sounds wrong to me because components should render and properties should be essentially models for the components/views. Maybe I am not understanding your terminology.The Ninja Space Goat wrote:Alright, I'm having some trouble here again. I'm hoping somebody can just give me a push in the right direction. I've tried several things, but none seem flexible enough. I've got qCal_Component and qCal_Property. Components can have other components, as well as properties as children. My renderer needs to be able to traverse this tree of qCal_Components / qCal_Properties and render them based on logic provided by subclasses of qCal_Renderer. Does that make sense?
(#10850)
- Kieran Huggins
- DevNet Master
- Posts: 3635
- Joined: Wed Dec 06, 2006 4:14 pm
- Location: Toronto, Canada
- Contact:
I believe component tree traversal should be embedded into the components themselves - that's what the composite pattern is about. And you could implement renderer as a visitor: something like this:This just doesn't feel like it will provide that kind of flexibility, and also I end up looping through component's children twice here... which feels wrong. DRY?
Code: Select all
<?php
interface qCal_Component_Visitor_Interface
{
public function visitComponent(qCal_Component $component);
public function visitProperty(qCal_Property $component);
}
class qCal_Renderer_icalendar implements qCal_Component_Visitor_Interface
{
protected function visitComponent(qCal_Component $component)
{
// render only that pertaining to component itself
}
protected function visitProperty(qCal_Property $component)
{
// render property
}
}
interface qCal_Component_Interface
{
public function traverse(qCal_Component_Visitor_Interface $visitor);
}
class qCal_Component implements qCal_Component_Interface
{
public function traverse(qCal_Component_Visitor_Interface $visitor)
{
$visitor->visitComponent($this);
foreach ($this->getChildren() as $child) {
$child->traverse($visitor);
}
}
}
class qCal_Property implements qCal_Component_Interface
{
public function traverse(qCal_Component_Visitor_Interface $visitor)
{
$visitor->visitProperty($this);
}
}
?>