SitePoint Sponsor

User Tag List

Results 1 to 16 of 16
  1. #1
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Polymorphic View classes for layout reuse - good or bad?

    In the projects I was recently working on, I am using an inheritance hierarchy of Page and Widget classes to allow a fine-grained reuse of markup-generating code.

    Now I'd like to get some feedback if this is generally a good idea, or what I can do better.


    I will try to give a simplified example. Now rip it apart!
    The basic idea is that the render() method calls a lot of other methods for specific pieces of the page. These other methods are defined and overridden in subclasses, thus customizing the look of the page.


    Code PHP:
    class PageWithHTML extends VisualComponent
    {
        function render() {
            header('Content-type: text/html;charset="utf-8"');
     
            ?><!DOCTYPE ....>
            <html ....>
            <head>
            <link rel="shortcut icon" href="<?=$this->getFaviconURL() ?>" />
            <?php
            $this->includeScriptfiles();
            $this->includeStylesheets();
            ?>
            </head>
            <body>
            <?php
            $this->body();
            ?>
            </body>
            </html><?php
        }
    }
     
    class PageWithSiteLayout extends PageWithHTML
    {
        function body() {
            ?>some html ...<?php
            $menu_items = $this->getMenuItems();
            foreach ($this->getMenuItems() as $key => $menu_item) {
                if ($this->getActiveMenuItemKey == $key) {
                    ?>show $menu_item, highlighted<?php
                } else {
                    ?>show $menu_item, not highlighted<?php
                }
            }
            ?>some more html ...<?php
            $this->leftSidebarContent();
            ?>some more html ...<?php
            $this->mainContent();
            ?>footer html ...<?php
        }
     
        function getMenuItems() {
            return array(
                'messages' => 'My Messages',
                'blog' => 'My Blog',
            );
        }
    }
     
    class BlogPage extends PageWithSiteLayout
    {
        function getActiveMenuItemKey() {
            return 'blog';
        }
     
        function mainContent() {
            ?>blogging is fun<?php
        }
    }

    and, for using this:

    Code PHP:
    $page = new BlogPage();
    $page->render();


    The trick with all the tiny methods is that they provide fine-grained extension points to modify the behaviour of the parent class.

    The great thing about all this:
    - I only have to redefine the things which are different from the base class. No boilerplate html, effective layout reuse.
    - It has the taste of "Inversion of Control", a concept which has my sympathy. It's the parent class method calling the subclass method, not the other way round.

    What makes me unhappy, especially if my methods become very small and fine-grained:
    - OOP PHP and HTML will be mingled all over the place. Designers will hate me for that, if they don't like PHP.
    - If I want to put the HTML into templates, I will end up with tons of micro template files.
    - While I don't have boilerplate html, I have a lot of boilerplate PHP: A lot of methods with big definition, but little information value.
    - The inheritance hierarchy can grow quite deep, with a high number of methods accumulating with each subclass.
    - Depending on behaviour defined in a base class makes things quite static. What if I want to replace the layout defined in PageWithSiteLayout with PageWithMySkin ?


    It gets more extreme in another example, where my class hierarchy is something like
    • ItemlistWithPagination extends ItemlistWidget
    • MailboxWidget extends ItemlistWithPagination
    • MailboxWidget_Received extends MailboxWidget
    • MailboxWidget_Sent extends MailboxWidget
    • MailboxWidget_Spam extends MailboxWidget

    or, for the forum it would be
    • ForumTopicWidget extends ItemlistWithPagination


    Methods in this example allow to redefine individual table cells, like this

    Code PHP:
    class ItemlistWidget
    {
        protected function showItems_table()
        {
            echo '
            <table>';
            // table headline
            if (!$this->hideColumnTitles()) {
                echo '
                <tr class="title">';
                foreach ($this->getTableColumns() as $key => $value) {
                    echo '
                    <th class="'.$key.'">' . $value . '</th>';
                }
                echo '
                </tr>';
            }
            // table rows with items
            $items = $this->getItems();
            if (!is_array($items) && !is_object($items)) {
                throw new PException('"ItemlistWidget::getItems()" must return an array or an object.');
            }
     
            $index = 0;
            foreach ($items as $itemkey => $item) {
                echo '
                <tr class="' . ($index%2 ? 'odd' : 'even') . '">';
                foreach ($this->getTableColumns() as $key => $value) {
                    $methodname = 'tableCell_'.$key;
                    echo '
                    <td class="'.$key.'">';
                    if (method_exists($this, $methodname)) {
                        $this->$methodname($item, $itemkey);
                    } else {
                        $this->tableCell($key, $item, $itemkey);
                    }
                    echo '
                    </td>';
                }
                echo '
                </tr>';
                ++$index;
            }
            echo '
            </table>';
        }
    }
     
    class ItemlistWithPagination extends ItemlistWidget {..}
     
    class MailboxWidget extends ItemlistWithPagination {..}
     
    class MailboxWidget_Sent extends MailboxWidget
    {
        protected function getTableColumns()
        {
            return array(
                'checkbox' => 'Select',
                'contact' => 'From/To',
                'title' => 'Text',
                'dateSent' => 'Date'
            );
        }
     
        protected function tableCell_dateSent($message)
        {
            ?>some html showing $message->date_sent..<?php
        }
    }

    As I read here and there (especially on SitePoint), experienced developers prefer composition over inheritance. How would the alternative look like?

    Any way one could combine this with skinning? Or caching of static page elements?

  2. #2
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lemon.head View Post
    - Depending on behaviour defined in a base class makes things quite static. What if I want to replace the layout defined in PageWithSiteLayout with PageWithMySkin ?
    The way I am currently doing this is by hacking the __autoload function. This mechanic works quite well, but I don't really like the idea, as it messes with a global state (the table of defined classes).

    Doing it with composition instead of inheritance would mean I need a clear interface between the parent class (defining the overall skinned site layout) and the child class (representing a specific page).

    What I would like to have is some kind of dynamic inheritance!

  3. #3
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lemon.head View Post
    The basic idea is that the render() method calls a lot of other methods for specific pieces of the page. These other methods are defined and overridden in subclasses, thus customizing the look of the page.
    The idea of separating different view components into distinct blocks of code (whether objects or functions) works well. I would suggest that you return output from your code, instead of outputting directly, and then let the top-level handle output. That makes it easier to manage your code, and also easier to reuse components.

    Quote Originally Posted by lemon.head View Post
    - The inheritance hierarchy can grow quite deep, with a high number of methods accumulating with each subclass.
    - Depending on behaviour defined in a base class makes things quite static. What if I want to replace the layout defined in PageWithSiteLayout with PageWithMySkin ?
    Traditionally, people have used static hierarchies (inheritance) for gui toolkits (Like this). As you note, it leads to deep hierarchies, which again means that you have to trace up and down in the hierarchy to figure out which code actually gets called in run time. It also makes impossible to change the hierarchy in run time. While initially slightly more verbose, a compositional style is much preferable to this bureaucracy. IMHO anyway.

    Quote Originally Posted by lemon.head View Post
    What I would like to have is some kind of dynamic inheritance!
    That would be composition.

    Quote Originally Posted by lemon.head View Post
    It has the taste of "Inversion of Control", a concept which has my sympathy. It's the parent class method calling the subclass method, not the other way round.
    Calling it inversion of control is probably stretching the term a bit far, but the general principle of decomposition is there all right. The next step could be to make the parent and the child separate objects.

    Quote Originally Posted by lemon.head View Post
    As I read here and there (especially on SitePoint), experienced developers prefer composition over inheritance. How would the alternative look like?
    As it is now, you essentially have multiple distinct components, locked in the same object. Split the class hierarchy into a number of classes, and relate them to each other through instances. For example:

    PHP Code:
    interface VisualComponent {
      function 
    render(Page $page);
    }

    interface 
    Page {
      function 
    addScriptFile($file);
      function 
    addStyleSheet($file);
      function 
    setFavIcon($fav_icon_url);
      function 
    getActiveMenuItem();
      function 
    setActiveMenuItem($name);
    }

    class 
    NoVisualComponent implements VisualComponent {
      function 
    render(Page $page) {
        return 
    "";
      }
    }

    class 
    HtmlPage implements Page {
      protected 
    $fav_icon_url "";
      protected 
    $script_files = array();
      protected 
    $style_sheets = array();
      protected 
    $active_menu_item;
      protected 
    $body;
      function 
    __construct(VisualComponent $body null) {
        
    $this->body $body $body : new NoVisualComponent();
      }
      function 
    getActiveMenuItem() {
        return 
    $this->active_menu_item;
      }
      function 
    setActiveMenuItem($name) {
        return 
    $this->active_menu_item $name;
      }
      function 
    addScriptFile($file) {
        
    $this->script_files[] = $file;
      }
      function 
    addStyleSheet($file) {
        
    $this->style_sheets[] = $file;
      }
      function 
    setFavIcon($fav_icon_url) {
        
    $this->fav_icon_url $fav_icon_url;
      }
      function 
    render() {
        
    $html_body $this->body->render($this);
        
    $html "<!DOCTYPE ....>
    <html ....>
      <head>
        <link rel=\"shortcut icon\" href=\"" 
    htmlspecialchars($this->fav_icon_url) . "\" />
    "
    ;
        foreach (
    $this->script_files as $script) {
          
    $html .= "    <script type=\"text/javascript\" href=\"" htmlspecialchars($script) . "\"></script>\n";
        }
        foreach (
    $this->style_sheets as $style) {
          
    $html .= "    <link rel=\"stylesheet\" href=\"" htmlspecialchars($style) . "\" />\n";
        }
        
    $html .= "  </head>\n";
        
    $html .= "  <body>\n";
        
    $html .= $html_body;
        
    $html .= "  </body>\n";
        
    $html .= "</html>";
        return 
    $html;
      }
    }

    class 
    SiteLayout implements VisualComponent {
      protected 
    $main_content;
      protected 
    $left_sidebar_content;
      protected 
    $menu_items = array();
      function 
    __construct(VisualComponent $main_content nullVisualComponent $left_sidebar_content null) {
        
    $this->main_content $main_content $main_content : new NoVisualComponent();
        
    $this->left_sidebar_content $left_sidebar_content $left_sidebar_content : new NoVisualComponent();
      }
      function 
    addMenuItem($name$title) {
        
    $this->menu_items[$name] = $title;
      }
      function 
    render(Page $page) {
        
    $html_main_content $this->main_content->render($page);
        
    $html_left_sidebar_content $this->left_sidebar_content->render($page);
        
    $html "some html ...";
        foreach (
    $this->menu_items as $name => $title) {
          if (
    $page->getActiveMenuItem() == $name) {
            
    $html .= htmlspecialchars($title) . " highlighted<br/>\n";
          } else {
            
    $html .= htmlspecialchars($title) . " not highlighted<br/>\n";
          }
        }
        
    $html .= "some more html ...";
        
    $html .= $html_main_content;
        
    $html .= "some more html ...";
        
    $html .= $html_left_sidebar_content;
        
    $html .= "footer html ...";
        return 
    $html;
      }
    }

    class 
    BlogContent implements VisualComponent {
      function 
    getActiveMenuItemKey() {
        return 
    'blog';
      }
      function 
    render(Page $page) {
        
    $page->setActiveMenuItem('blog');
        return 
    "blogging is fun";
      }
    }

    $page = new HtmlPage(
      new 
    SiteLayout(
        new 
    BlogContent()));

    echo 
    $page->render(); 

  4. #4
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks a lot!
    I'll give that composition idea more thought.

    What I would like to have is some kind of dynamic inheritance!
    That would be composition.
    Hmm.
    I played a bit trying to simulate dynamic inheritance (or should I say mixins?) using magic methods (__call). And yes, it works. I don't know about performance penalties, though..

    What I like with inheritance is that I can override or call methods up and down the hierarchy line, without having to know in which layer the method I want is actually defined. For instance, in one page it might be sufficient to only override the main content, while in another page I want to replace the entire menu.

  5. #5
    SitePoint Zealot
    Join Date
    May 2008
    Location
    Montreal
    Posts
    155
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    One thing that I've recently liked doing is to have view objects as simple dictionaries that have a file name. The file name they link to can be changed at any time without changing the data stored in the dictionary. Then when the view is called on to render, if the file they have exists then they include that file, with the dictionary of variables exported to that scope. I also make available of a $scope variable through the render variable which is a stack of dictionaries of all parent views' scope data, giving lexical scoping of sorts.

  6. #6
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Sorry, I'm a bit lost.

    I assume the file is a template file using some variables set by the view object. The view object gets these variables injected from the controller. And the view object is part of a parent/child hierarchy? How does that hierarchy look like? And, do you only have one template file per view class? What does the view class represent - the entire page, or just a part of it?

  7. #7
    SitePoint Zealot
    Join Date
    May 2008
    Location
    Montreal
    Posts
    155
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A template object is a lot like an php's SPL ArrayObject. Alone, it has no explicit links to any parent templates. A template object can have at most one file name that it will include (it is a php/html page). Template objects, in their variables, can have template objects than can be rendered. The template objects are really quick "stupid" to the extent that they could be an entire page or just a part of a part. Really, that is determined by the template itself and not the template object. The way I have things structured is that I provide, by default, two template objects: one that is a layout and another that is a page. The page template object is simpler a variable within the layout object and is thus rendered within the template that the layout template object points to.

  8. #8
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @Kyberfabrikken:

    The problem I see with composition is who or what is responsible for putting it together. A page is not sufficiently described by its pieces, but also which piece goes where. As I see it, in your draft this happens in the controller.

    PHP Code:
    $page = new HtmlPage(
      new 
    SiteLayout(
        new 
    BlogContent(),
        new 
    BlogSidebar(),
        new 
    BlogFooter()
      )
    );
    $page->setPageTitle($blog_title); 
    For my taste, the controller does far too much, if its original responsibility is to map a request to a page, and decide if the user is allowed to see the page or not.

    Imo, the composition of view components should happen in the view layer itself. Which subclasses do in a convenient way.

    In my approach, each method knows where it is to be displayed on the page. Well, not really, but the method name defines the "role" of the method - the caller can still choose to show the leftSidebar on the right instead. If I want to remap the roles of the methods, I will throw in an adapter layer.

    In your compositional approach, neither the method name nor the class name have an information value, you need to get that information in from outside instead.


    As it is now, you essentially have multiple distinct components, locked in the same object. Split the class hierarchy into a number of classes, and relate them to each other through instances.
    A general question with composition is where you want to cut:
    1. One possible philosophy is to see the page elements as components, and put them into classes.
    2. Another one is to analyse which pieces of layout are shared by two pages, and make a partition based on this observation. In my approach, these are the inheritance layers. Each layer can be seen as a collection of DOM replacement rules, and two such rules can be related even if they affect different pieces of the page. This philosophy will result in good layout reuse, where nothing needs to be defined twice.
    3. A third way can be to group layout elements by the resources they use: A forum thread with pagination, etc. This will give a good data encapsulation.

    In my experience, 2. and 3. way can play together quite well. If the components are separated this way, composition is more a job of logic, which fits quite well into the controller: Which skin is used, which page is shown, which data does this page need.

    With 1., composition means to arrange elements on the page - which is a typical job for the view layer.

    I would suggest that you return output from your code, instead of outputting directly, and then let the top-level handle output.
    What about loops? I can make a method in a subclass which overrides just a little piece of code which is called in a loop. You can hardly pass this information as a string!


    Traditionally, people have used static hierarchies (inheritance) for gui toolkits (Like this). As you note, it leads to deep hierarchies, which again means that you have to trace up and down in the hierarchy to figure out which code actually gets called in run time. It also makes impossible to change the hierarchy in run time. While initially slightly more verbose, a compositional style is much preferable to this bureaucracy. IMHO anyway.
    The problems I see with my original approach:
    • native PHP class inheritance is a very static concept (-> agree with you on that)
    • the layers are not isolated from each other. (-> again, agree with you)
    • the layout definition happens in the same place as the layout composition. However, I don't know if that is really a drawback. At least, it gives a benefit in information efficiency, as I don't have that much zero-information code like "render()". I still have to see how I would lose flexibility by letting the pieces know their default roles.


    So, the main challenge now is how I can isolate the layers, and how I can compose layers in a more dynamic way than with static inheritance. And deciding which parts of that composition should happen where, and where the layers should get their data from.

    What I imagine is some kind of self-constructed dynamic inheritance with somewhat isolated layers. Then I am free to sandwich in new layers based on skin, user preferences etc. But, this does not answer the complete question, unfortunately.

    I know magic methods can do a lot of that,

  9. #9
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I would suggest that you return output from your code, instead of outputting directly, and then let the top-level handle output.
    What about loops? I can make a method in a subclass which overrides just a little piece of code which is called in a loop. You can hardly pass this information as a string!
    Clarification: I am talking here about the way you exchange information between the different layout objects. You can still wrap your entire code in an output buffer, and do whatever with the output..

    In my code it is enough to wrap the controller in an output buffer. The controller sends a response, which is usually a page to be rendered - and from that point, I don't see any argument for further output buffering. Especially, this allows to see errors in the location of the page where they relate to, and not in some place where a string is set - makes debugging a lot easier.

  10. #10
    SitePoint Addict Mastodont's Avatar
    Join Date
    Mar 2007
    Location
    Czech Republic
    Posts
    375
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lemon.head View Post
    @Kyberfabrikken:
    A page is not sufficiently described by its pieces, but also which piece goes where.
    This should be determined by page template (static HTML with some tags for pieces), shouldn't it?

  11. #11
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lemon.head View Post
    The problem I see with composition is who or what is responsible for putting it together. A page is not sufficiently described by its pieces, but ...
    Yes, I kind of skipped over that part. You can encapsulate the wiring in a factory of some kind. At this level, a static relationship between controller and view is probably fine, so you could do something like:
    PHP Code:
    class BlogPageFactory {
      function 
    create($blog_title) {
        
    $page = new HtmlPage(
          new 
    SiteLayout(
            new 
    BlogContent(),
            new 
    BlogSidebar(),
            new 
    BlogFooter()
          )
        );
        
    $page->setPageTitle($blog_title);
        return 
    $page;
      }

    and then in your controller:
    PHP Code:
    $view = new BlogPageFactory();
    $page $view->create();
    echo 
    $page->render(); 

  12. #12
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    hmmmm...
    and then i need a new factory + component classes for each case where i currently have just a simple "extends" ? This would be

    MessagesMenu

    MessagesInboxPageFactory
    MessagesInboxMainContent
    MessagesInboxTable
    MessagesInboxTableCell

    MessagesOutboxFactory
    MessagesOutboxMainContent

    MessagesSpamboxFactory
    MessagesSpamboxMainContent

    This sounds a lot like overkill, and it is a pain from an information efficiency point of view.

    I like flexibility, but even better is having a zero-redundancy default version with a zero-pain flexibility backdoor. Such as, the painless switch from a public attribute to magic methods, instead of explicit setters and getters.

    Maybe one could automate the factory a bit, so we would need only one, but still...

  13. #13
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Mastodont View Post
    This should be determined by page template (static HTML with some tags for pieces), shouldn't it?
    The page template has named "slots" for content elements. But at some point you have to fill these slots. So in fact the mapping of what-goes-where happens twice! Well, you gain some flexibility this way..

  14. #14
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lemon.head View Post
    and then i need a new factory + component classes for each case where i currently have just a simple "extends" ?
    ...
    This sounds a lot like overkill, and it is a pain from an information efficiency point of view.
    You'll get one more class, but it serves a purpose, so I don't think it's information overkill at all. In fact, I would argue that the code is more clear to read (Even if it takes more effort to write), because the relationships are explicit and have names.

    To a large extend, I think it's also a matter of mindset. You are used to classes being heavy components in your code today, so adding one feels like a big thing to do. If you're used to thinking of classes as many and small, it's not as overwhelming any more. To draw an analogy, some people get overwhelmed by multiple functions. So they'd rather have a few, complex functions, than many, more fine grained ones.

    Quote Originally Posted by lemon.head View Post
    Maybe one could automate the factory a bit, so we would need only one, but still...
    You can use a reflection based container if you wish; That makes the wiring happen almost invisibly. I kind of like the more manual approach, even if it is a bit more laborious, since it's much easier to follow.

  15. #15
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You are used to classes being heavy components in your code today, so adding one feels like a big thing to do.
    The project's classes have been a lot bigger before I introduced the mechanic
    Also depends what you see as the size of the class: Combined with the parent class, it is very big, yes. If you only look at the subclass definition, most of them are really nice and small. This is why I am thinking about a way to isolate the layers - not the DOM elements.

    What looks like redundancy for me is mostly the idea of having a Room666 and a KeyToRoom666, and then have to explicitly connect both of them, even if KeyToRoom666 would not fit anywhere else. If I need any flexibility in such a case, I would like it to be less explicit.

    relationships are explicit and have names
    That's exactly a point where I am not feeling comfortable with. The best example being explicit setters and getters vs magic methods.
    I already observed myself spending lots of time to write setters and getters and private or protected attributes, only to come to the conclusion that the interface sucks, and I have to do it again. And after doing that, I totally lost the original idea behind the work. And, if you handcraft a flawed interface this way, you will hesitate to improve it.

    Right now I'm using interfaces which are mostly invisible, and require almost no work. If I need an injected property, I simply use it as-if it existed. Then when PHP fires an error because of a missing attribute, I go to the controller and set the attribute. If I need a filter, I use magic method hooks.

    Only when I believe that the interface will not change any more that soon, I start to think about making it explicit. But usually a look into the controller, where the class is actually used, is a lot more helpful than looking at a setter/getter interface.

  16. #16
    SitePoint Enthusiast
    Join Date
    Jul 2008
    Posts
    53
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Well, maybe you are right, I just need to think more about it.

    That said, I am still curious what is the recommended strategy to split the view into pieces! The DOM ?

    Quote Originally Posted by myself
    A general question with composition is where you want to cut:

    1. One possible philosophy is to see the page elements as components, and put them into classes.
    2. Another one is to analyse which pieces of layout are shared by two pages, and make a partition based on this observation. In my approach, these are the inheritance layers. Each layer can be seen as a collection of DOM replacement rules, and two such rules can be related even if they affect different pieces of the page. This philosophy will result in good layout reuse, where nothing needs to be defined twice.
    3. A third way can be to group layout elements by the resources they use: A forum thread with pagination, etc. This will give a good data encapsulation.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •