Trying to internalize object orientation
Trying to internalize object orientation
Trying to internalize object orientation
Trying to internalize object orientation
Trying to internalize object orientation
Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation
Trying to internalize object orientation Trying to internalize object orientation
Trying to internalize object orientation
Go Back  Xtreme Visual Basic Talk > > > Trying to internalize object orientation


Reply
 
Thread Tools Display Modes
  #1  
Old 03-11-2010, 12:38 AM
habrams habrams is offline
Newcomer
 
Join Date: Dec 2007
Location: Atlanta, GA
Posts: 11
Question Trying to internalize object orientation


I learned to program almost 40 years ago, long before object oriented languages existed. I continue to solve problems in a procedurally-oriented way, so that my programs lack elegance and simplicity; they look kludgy. So I set myself a problem to solve using an object-oriented approach.

Here is the task. I want to create a TreeView control that accepts structures rather than strings as its node elements. These structures will include a string field, and it is this string field that will be displayed. Seems simple enough.

If I subclass the TreeView control directly, I also have to subclass the TreeNode class. And then I have to write all new display and editing methods, losing almost all the benefit of inheritance. Or so it seems to me.

So then I thought I would just subclass the string class, but since I cannot know how the TreeView controls displayes and edits its string nodes without violating encapsulation, that gets me nowhere. The same analysis seems true if I try to subclass the TreeNode class.

Am I missing something, or does object orientation offer little advantge when trying to inherit from a complex, visual object? I understand how to subclass Dog to get a Collie, but most of the books stop there or with a similarly too-simple example.

I'm not looking for coding help directly but rather with the metacoding. Again, this is just an exercise I set for myself.

Any help would be greatly appreciated.

Howard
Reply With Quote
  #2  
Old 03-11-2010, 06:26 AM
PlausiblyDamp's Avatar
PlausiblyDampTrying to internalize object orientation PlausiblyDamp is offline
Ultimate Contributor

Forum Leader
* Expert *
 
Join Date: Nov 2003
Location: Newport, Wales
Posts: 2,058
Default

Could you not just sub class the TreeNode class rather than both the TreeView and the TreeNode?

The overloaded methods of the TreeView that accept strings are just a convenience, behind the scenes it uses TreeNodes and there should be overloads that accept a TreeNode or sub class of a TreeNode.

You could then extend your tree node class to include either the structure or the extra information in the structure, overriding the ToString() of the TreeNode method should also allow you to control the string displayed in the TreeView.
__________________
Intellectuals solve problems; geniuses prevent them.
-- Albert Einstein

Posting Guidelines Forum Rules Use the code tags
Reply With Quote
  #3  
Old 03-11-2010, 10:13 PM
habrams habrams is offline
Newcomer
 
Join Date: Dec 2007
Location: Atlanta, GA
Posts: 11
Default

But aren't you assuming you know how a Treeview displays the string of a Treenode? And doesn't that violate encapsulation? In fact, if this is the right technique, why not just subclass the String class by adding fields (to create, in effect, my structure) and then override the ToString method of the String class? More generally, when ComplexObject includes a SimpleObject as a field that is manipulated by methods of ComplexObject, should not (at least in theory) that prevent using a child of SimpleObject with overriding methods?

-- Howard
Reply With Quote
  #4  
Old 03-12-2010, 06:19 AM
PlausiblyDamp's Avatar
PlausiblyDampTrying to internalize object orientation PlausiblyDamp is offline
Ultimate Contributor

Forum Leader
* Expert *
 
Join Date: Nov 2003
Location: Newport, Wales
Posts: 2,058
Default

Looking into it a bit more it seems the TreeView uses the TreeNode's .Text property as the string to display (TreeNode on MSDN) this effectively makes it part of the contract between a TreeView and a TreeNode and there is no assumption being made.

If you create a simple form and add one treeview, two textboxes and two buttons then paste the following into the code behind
Code:
Option Strict On
Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim p As New Person
        p.FirstName = TextBox1.Text
        p.LastName = TextBox2.Text

        Dim pn As New PersonTreeNode(p)
        TreeView1.Nodes.Add(pn)
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim p As Person
        p = DirectCast(TreeView1.SelectedNode, PersonTreeNode).Person

    End Sub
End Class

Public Class Person
    Public FirstName As String
    Public LastName As String

    Public Overrides Function ToString() As String
        Return FirstName & " " & LastName
    End Function
End Class

Public Class PersonTreeNode
    Inherits TreeNode

    Private _person As Person

    Public Sub New(ByVal person As Person)
        Me.Text = person.ToString
        _person = person
    End Sub

    Public ReadOnly Property Person() As Person
        Get
            Return _person
        End Get
    End Property

End Class
It defines a simple Person class and a PersonTreeNode to encapsulate this class when dealing with the TreeView. The PersonTreeNode allows the TreeView to work with our Person class without the Tree itself having any knowledge of how a person works while also providing an easy way for us to retrieve the underlying person object.
__________________
Intellectuals solve problems; geniuses prevent them.
-- Albert Einstein

Posting Guidelines Forum Rules Use the code tags
Reply With Quote
  #5  
Old 03-12-2010, 11:56 AM
AtmaWeapon's Avatar
AtmaWeaponTrying to internalize object orientation AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

This will not be a short reply.

Object-oriented analysis and design (OOD) is a big topic. OOD is not a magic bullet. You can design an application using what looks like good OOD and end up with a complicated, unmaintainable mess. The novice in OOD latches on to "objects = abstractions of real things" really fast. Unfortunately, there are other and more important parts of OOD. OOD is a powerful tool and like many powerful tools if you use it wrong you will make things worse. When used properly, OOD partitions your logic in such a way that changes you make to your code will not break things that depend upon your component. When used improperly, OOD creates a situation where minor changes mean you have to rebuild and test every component you've ever written.

Controls are a bad place to start learning OOD. Most have very complicated rendering logic that can't be modified. The controls that allow you to modify their rendering behavior do so through the OwnerDraw pattern and don't use inheritance! The rest of the controls require that you override OnPaint() (or handle the Paint method) and write your own rendering logic. If you're completely replacing the logic when you derive, it's likely that you shouldn't be using inheritance (for controls sometimes it is OK.) In general, inheritance should be about a behavioral relationship, not "I need these properties".

Instead of random projects, I suggest some reading. Start with Head First Object-Oriented Analysis & Design. It teaches the fundamentals of how to approach problems using OOD. From there, learn more about how to make *good* object-oriented designs. Head First Design Patterns does a surprisingly good job of supplementing a basic OOD knowledge with more advanced design techniques. This book is a good introduction to the Gang of Four book, a must-read that is dry and technical (save it for later.) I'm finishing up Agile Principles, Patterns, and Practices in C# and the first 1/3 of the book outlines what most people agree are the fundamental principles good object-oriented designs follow. Martin is famous for his SOLID principles.

These books use Java and C#. They only use features of the languages that have direct correlation to VB .NET language features. The books teach fundamental principles of object-oriented design; it's worth learning the 10 keywords worth of Java and C# you need to read them. You will learn more about OOD from spending 3 months on these books than I did in 6 years of experimenting to see what worked.

On-topic:
Quote:
But aren't you assuming you know how a Treeview displays the string of a Treenode? And doesn't that violate encapsulation?
There is no assumption, there is documented fact. TreeNode.Text's documentation states:
Quote:
Property Value
Type: System.String
The text displayed in the label of the tree node.
This is not a violation of encapsulation because if TreeView did *not* expose the details of how to set the text it will display for a TreeNode it would be useless. The main use case is "display a node with text that I specify"; if the control hid how to update this text behind a veil of secrecy you'd be left with a horrible interface. You need a documented and stable way to set this text. It would be a violation of encapsulation to hide this detail.

Quote:
In fact, if this is the right technique, why not just subclass the String class by adding fields (to create, in effect, my structure) and then override the ToString method of the String class?
Several reasons. First, it's not the right technique. Next, System.String is a sealed class; you cannot inherit from it. Let's pretend it is. System.String is a primitive value, which means it representation *is* its value. In other words, you don't call ToString() to get a String, you just use a String. Think about Integer. Do you write code like this?
Code:
Function DoubleThis(ByVal input As Integer) As Integer
  Return input.Value * 2.Value
No, you'd write it by just using input * 2. Does it make any more sense to have to call ToString() on something that you know is a String every time? If you were allowed to make your derived custom String, you would have to. Once you introduce properties that have to be combined to produce the value, String stops being a primitive and starts being a composite type. This would violate the contract of String.

Quote:
More generally, when ComplexObject includes a SimpleObject as a field that is manipulated by methods of ComplexObject, should not (at least in theory) that prevent using a child of SimpleObject with overriding methods?
Not while polymorphism works. The long answer to this question is fun, and involves the two attached projects.

The short answer is it is *wrong* for classes derived from SimpleObject to behave in a manner that is different than SimpleObject (see Liskov Substitution Principle.) If code has to know that it's dealing with a derived version there's a very high probability your design is wrong. If ComplexObject needs to use a type with *more* behavior than SimpleObject, then it is more wise to derive/create a new class that uses CustomSimpleObject. When inheritance/polymorphism are used properly, the derived class looks no different than the base class and behaves in a compatible manner.

Let's say you have a need to represent a list of integer values. Every time a clock ticks, you want to perform an operation on the values, but the operation may be different for each value. You also want each item to keep track of how many times the value has been changed. You also need a guarantee that the value changes each time. For example, you might have two values and want to double the first number and add 1 to the second number each tick; if we put the output in the format "clock tick/value", it might produce this output:
Code:
1/1 1/1
2/2 2/2
3/4 3/3
4/8 4/4
...
It should be illegal for something to produce the same value twice, for example the Fibonacci sequence starts "1, 1, 2, 3..." and the duplicated 1 should not be allowed.

In the "Polymorphism" project, the class ChangingValue implements the base class for a type that does this. The ClockTick property represents the current clock tick. The Value property represents the current value and enforces that each new value should be different. The ChangeValue() method is used to change the value. Classes can derive from ChangingValue and override ChangeValue() to implement their own behavior. DoublingValue is an example of a class that does this properly. It overrides ChangingValue, calls the base implementation, then changes the value by doubling it.

But not all is well with this design. The contract of ChangingValue requires that the clock tick be updated every time the value is changed, but if the user forgets to call MyBase.ChangeValue() this won't happen. BrokenClockValue demonstrates this:
Code:
----- Broken Clock -----
0/1
0/2
0/3
0/4
0/5
0/6
0/7
This is one of the problems with allowing inheritance: you *cannot* guarantee that derived classes will call the base class implementation. If something in your class depends on something that the base implementation does, you have to turn to other means I will explain later.

That's not the only problem. The "enforce changing value" logic has a flaw as well. DoesNotChangeValue demonstrates. It doesn't override ChangeValue(), which means it never tries to set the value and the exception won't be thrown. Whoops! Since ChangeValue() isn't MustOverride, we can't force users to override it. If we make it MustOverride, users are responsible for updating the clock tick; if they forget to do it we can't detect it because we can't provide a base implementation for a MustOverride method. What do we do?

I'm out of characters, halp! Transitioning to a new post...
Attached Files
File Type: zip Polymorphism.zip (9.9 KB, 1 views)
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #6  
Old 03-12-2010, 12:00 PM
AtmaWeapon's Avatar
AtmaWeaponTrying to internalize object orientation AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

The TemplateMethod project solves the problem using a pattern called "Template Method". The Template Method pattern is a way to implement an algorithm that has parts that should never change and parts that can be influenced by derived classes. The pattern looks like this:
Code:
Public Sub Algorithm()
  StuffThatShouldAlwaysHappen()
  
  DerivedStuff()
  
  StuffThatShouldAlwaysHappen()
End Sub

Protected Overridable Sub DerivedStuff()
  ' ...
End Sub
Since Algorithm() isn't overridable, users can't stop the things that are in it from happening. Users *can* control what happens by overriding DerivedStuff(), but you get a chance to validate what they did in Algorithm().

The "TemplateMethod" project demonstrates this. Notice the small changes to ChangingValue(); I made Value's setter private because I don't want derived classes to change it without going through the protected GetNewValue(). I also changed ChangeValue() to use the Template Method pattern. BrokenClockValue is now impossible to implement with its flaw because the clock tick is enforced by the not-overridable ChangeValue(). Sure, a user could use Shadows to provide their own ChangeValue(), but we can't stop maniacs who want to shoot themselves in the foot. DoesNotChangeValue now properly causes an exception to be thrown.

Both methods work because even though the derived classes have custom properties, all the main program needs to know is "call ChangeValue()". The only time I need to mess with the properties is when I create the object; from that point on as far as the program cares it just needs something that has a ChangeValue() method. If everything had some kind of properties for configuration and the main program needed to continually update the configuration, I'd consider finding a way to move the configuration into the base class. This is the ideal; you can't always do it but you should have a reason if you don't.

The point of the example is that there are very rigid requirements that are placed on derived classes. If you find that the base class object is good, but you're constantly relying on derived-class functionality, this is a sign that your base class is poorly designed. If ComplexObject only knows about SimpleObject, then it can only use SimpleObject members. If you want to depend on DerivedSimpleObject, it is best to derive/write a new ComplexObject that depends on DerivedSimpleObject and may not be able to use SimpleObject. You *could* make ComplexObject check type information and do something special for DerivedSimpleObject. This is a violation of the Open-Closed Principle and should not be done casually.

The "PoorDesign" project demonstrates this. In this project, only the ClockTick and Value properties are captured in the base class. The "change value" part of code is left to derived classes to implement. Because of this decision, UpdateValue() in the main program has to follow a silly algorithm. First, it updates the clock tick because this is base class functionality. Then, it figures out what type of derived object it has. Then, it does something specific to that derived object. This is bad because it is not going to respond well to change. What happens when we add a SubtractingValue? We have to modify UpdateValue() and anything else in the program that tries to work on Value in a generic manner. That could be dozens of methods spread across as many files, and if we miss just one that's a bug we just added! It's much smarter to realize that "change the value" is something we want to do with every Value and make that part of the base class so that we can treat every object the same.

I'm sure you're rolling your eyes now, *of course* PoorDesign is bad, I just detailed how there's better solutions. But let's consider what you want out of TreeView. You want to create a CustomTreeNode that has behavior different from TreeNode. You want a TreeView that accepts only these types. The TreeView.Nodes property can't be overridden, so you can't force it to only accept your node types. You *could* shadow it, but your efforts could easily be subverted with a cast from CustomTreeView to TreeView. So in the end, all of your logic will have to ask, "Are you a CustomTreeNode? If so, do this special stuff..." and we're in the same boat as PoorDesign!

Unfortunately, there is no way to extend TreeView to safely support an extended TreeNode type. Derivation doesn't work because you can't force the use of custom nodes through anything but convention. You could put your data in the Tag property of each node, but you can't enforce the rule, "Every node should have this data type in its Tag property" so you still need to be prepared for non-custom nodes. You could use an external collection that associates nodes with a data structure, but this is just a more complicated version of "use the Tag property". You can have a custom TreeNode type that encapsulates your data type, like PlausiblyDamp's solution (this is what I usually do in this situation); this is still analagous to a Tag property because the TreeView has to verify it has a CustomTreeNode before it can use its specific properties. In short, you *can't* make a custom node type without requiring some kind of "are you what I expect" code.

If you can't do this without violating good OOD principles, why am I complaining? Sometimes we have to break the rules to get things done. It's very important that we understand that we are breaking the rules and that we know there is no other choice. If you were using TreeView to learn "good" OOD practices, you would learn practices that should be avoided when you design your own classes! This is why I recommend reading books, then practicing. Sometimes practice teaches us bad habits. In particular, controls do not lend themselves to extensibility through inheritance. Trying to learn OOD by studying controls is like trying to learn how to build a table by studying a skyscraper. Sure, some of the techniques are the same, but the engineering goals are so different that you'll wind up studying tons of things that have nothing to do with what you want.

In this ridiculously long post, you got a taste of the Command pattern, the Template Method pattern, the Liskov Substitution Principle, and the Open-Closed principle. You also got some book suggestions to help you figure out where those apply. I really think you'll learn more from reading the books than trying to make derived controls; in general the classes in most of the .NET Framework aren't well-designed for extensibility through inheritance.
Attached Files
File Type: zip TemplateMethod.zip (10.1 KB, 0 views)
File Type: zip PoorDesign.zip (9.5 KB, 0 views)
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #7  
Old 03-12-2010, 11:01 PM
habrams habrams is offline
Newcomer
 
Join Date: Dec 2007
Location: Atlanta, GA
Posts: 11
Default Two excellent replies. Thank you both very much.

It took me a while to appreciate the wisdom of the initial reply, but I finally understood its implications. Of course, the code example (slightly different from the solution I derived from the first reply) made it clearer still. Thank you for two good answers.

The longer reply on the theory of OOP (divided across two messages) is much appreciated; I know it took significant time to write. I read it closely and will read it again. Two quick observations. First, in VB.NET, everything is an object including primitives. Thus, using a string as a primitive should be the equivalent of using MyString.ToString (ditto with the .value of an integer). But if someone displays a string by interating though the string as a vector of characters, all hell will break loose. OOP should protect me from that. (And it does; see the code example offered in the post above.) Second, I own and have read many (many) books on OOP including books for Delphi programmers, VB programmers, and C# programmers. Since I am by trade an academic, I'm a big fan of learning by reading. The problem was not that I failed to read but that I failed to incorporate the teaching in a sufficiently deep way (the advanced age problem). This thread has been very helpful, and I very much appreciate the effort both responders put forth for my benefit. One of the many things I like about programming is the willingness of strangers to assist others. Thank you both.

Howard
Reply With Quote
  #8  
Old 03-13-2010, 11:57 PM
AtmaWeapon's Avatar
AtmaWeaponTrying to internalize object orientation AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Quote:
Originally Posted by habrams View Post
Two quick observations. First, in VB.NET, everything is an object including primitives. Thus, using a string as a primitive should be the equivalent of using MyString.ToString (ditto with the .value of an integer). But if someone displays a string by interating though the string as a vector of characters, all hell will break loose. OOP should protect me from that. (And it does; see the code example offered in the post above.)
You're seeing the reason why it's important for String to be closed to extensibility here. It's a type that nearly every other type in the .NET Framework depends upon, which means its contract is not just set in stone, it's more like it's etched in diamond. If you were allowed to derive from it, it'd be hard to make minor changes. A change to how the value is represented means that ToString(), the Chars property, and dozens of other methods need to behave, so any minor change could lead to you overriding the entire stack of methods. Get one method wrong, and suddenly it looks like parts of the .NET Framework are buggy when you pass them your custom strings. OOD should and does protect you from it: the designers realized how hard it would be to modify String without breaking it, so they prevented you from modifying it at all.

The observation that using the primitive value is the same as using ToString() may not be correct. String is a really complicated type and a bad example for this discussion because while it is immutable and primitive like a value type, it is defined as a reference type. My guess at what the implementation would look like would have ToString() returning a new copy of the value type, which means that calling ToString() is less efficient than using the primitive value. However, it's very difficult to test this for two technical reasons. First, the string interning feature means that the framework will go out of its way to treat strings like reference types in certain circumstances; if the string is interned then ToString() might display reference semantics. Second, primitive types are typically implemented in native code, so there's all manner of dirty tricks that ToString() could be doing. I really don't want to jump down that rabbit hole.

Quote:
Second, I own and have read many (many) books on OOP including books for Delphi programmers, VB programmers, and C# programmers. Since I am by trade an academic, I'm a big fan of learning by reading. The problem was not that I failed to read but that I failed to incorporate the teaching in a sufficiently deep way (the advanced age problem). This thread has been very helpful, and I very much appreciate the effort both responders put forth for my benefit. One of the many things I like about programming is the willingness of strangers to assist others. Thank you both.
Howard
Really I think the problem is that language-oriented books usually don't teech good OOD principles. Design patterns, SOLID principles, and other aspects of OOD transcend all OO languages. Language-oriented books don't want to repeat what's already been written, so they focus on how the language supports the pillars (inheritance, polymorphism, encapsulation, etc.) and assume if you want the rest of OOD you'll read another book.

I learned what I feel is a golden rule the hard way as I built a library of 40+ programming books where at least 25 of them are useless: Books that focus on a specific topic in a specific language are only useful for novices or intermediate users of that one topic. For example, I've got 3 or 4 VB .NET WinForms books and 2 or 3 C# WinForms books. The overlap between them is almost 100%. I kept buying them because I thought that one or the other would have information about some design patterns or advanced coding practices that would help me get farther. Nope. They're all oriented towards describing the tools of WinForms based on the assumption that if I had wanted a lesson on UI Design Patterns I would have bought a book about UI Design Patterns.

This is why lately I go out of my way to find language-agnostic books. The best example I can think of is Steve McConnell's Code Complete. It's a big book about writing good structured code. The examples are written in a random sampling of Visual Basic, C#, Java, Pascal, C, and maybe a couple of other structured/OO languages. This is done so that you aren't learning "how to write a good VB If statement" but "how to write a good If statement in any language with If statements". It's a fantastic read, though not as influential as some of the books on the agile approach to OOD. The books I suggested in my other approach tend to focus on C-style languages like Java, but it's mainly because they wanted consistency. 99% of their content is not "how to write good Java code" but "how to write good OO code". If you understand how your language implements OO concepts, it's easy to apply everything in the books to your language. Music is a good analogy. I can only understand English, but when I hear songs in French, German, Japanese, or any other language I can usually tell the difference between good music and bad music. While the sounds used to express the meaning are affected by the language, there are certain rules that govern what the ears consider pleasing and if they are not followed the brain rejects it as noise. Language-agnostic books teach you what good music sounds like in any language. Language-focused books teach you how to write good lyrics in a particular language.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #9  
Old 03-15-2010, 01:20 AM
habrams habrams is offline
Newcomer
 
Join Date: Dec 2007
Location: Atlanta, GA
Posts: 11
Default

Code Complete is a GREAT book, and one of two that I always recommend to a new programmer (the other is The Design of Everyday Things).

Interesting music metaphor and a nice explanation of why the string class is sealed.

Thanks again for your considerable help.

Howard
Reply With Quote
  #10  
Old 03-16-2010, 08:20 AM
clweeks clweeks is offline
Freshman
 
Join Date: Mar 2008
Posts: 33
Default

Just wanted to let y'all know that I've specifically found this useful. I've been reading Uncle Bob and SOLID and trying to teach myself how to apply that stuff to my VB.NET work. I even posted a thread about that at StackOverflow ( http://stackoverflow.com/questions/1...t-how-to-learn ) but I didn't think of coming here...silly me. Anyway, thanks!
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
Trying to internalize object orientation
Trying to internalize object orientation
Trying to internalize object orientation Trying to internalize object orientation
Trying to internalize object orientation
Trying to internalize object orientation
Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation Trying to internalize object orientation
Trying to internalize object orientation
Trying to internalize object orientation
 
Trying to internalize object orientation
Trying to internalize object orientation
 
-->