What to do to prevent God Object? - vb.net

I know that it is poor programming and architecture when you have a class object that is only to be used in one place. But I've also been warned about creating an object that is all powerful and that can do too much. So how do I break this down? Here is an example of what I mean - please don't take these things literal as this is only an example.
Anyway I have an object that I am working with which is rather complex. A lot of information is stored in this object and it can perform much manipulation on the data. So, let's call this object Earth.
Public Class Planet
Private _population As UInteger = 0
Public ReadOnly Property Population() As UInteger
Get
Return _population
End Get
End Property
Public Overridable Sub CreatePerson(Optional ByVal numberOfPeople As Integer = 1)
_population += numberOfPeople
End Sub
End Class
Simple enough so far. But I could go on and on with the many things that object could possibly perform. So, in order to keep things from getting too complex I broke down "activites" that would happen during the day and during the night by creating two other Objects: Day and Night (these two are not shown). So now I have an updated Planet class.
Public Class Planet
Private _population As UInteger = 0
Private _day As New Day
Private _night As New Night
Public ReadOnly Property Day() As Day
Get
Return _day
End Get
End Property
Public ReadOnly Property Night() As Night
Get
Return _night
End Get
End Property
Public ReadOnly Property Population() As UInteger
Get
Return _population
End Get
End Property
Public Overridable Sub CreatePerson(Optional ByVal numberOfPeople As Integer = 1)
_population += numberOfPeople
End Sub
End Class
Now, these two classes - Day and Night - will never be used outside of the Planet class. Is this a good way to organize my methods and attributes for this "parent" class Planet? How else would I neatly organize similar?
I've read about refactoring but I don't think this helps my case. I like the idea that I can call on an Planet object like this: Earth.Night.BlowUpMoon.

Think in terms of discoverability. If someone else were to use your object would they know that they have to go to a specific time of day to blow up the moon, which is the same as BirthdayCard.September25th.Send()? Any by "someone else" I also include you in 6 months. Are you organizing for the sake of organizing or are you putting similar methods and properties together in a way that makes sense?

Although your example is contrived, this situation is common practive in Domain Driven Design. Your Planet class would be an aggregate - a root object that manages its own internal entities. Outside the aggregate boundary all interaction is via the root aggregate object.

Refactor your class and split it to several smaller classes, each with a single responsibility. It doesn't matter that each of them will only be used once - the code will still be better, easier to understand, and far more testable.

Related

Serializing shared field

I have one class with a private static (shared, since I'm in VB.NET) field and its associated public static property, since it stores one variable that should be the same to all the instances of this class. So far, so good.
The problem arrives when trying to binary serialize these kind of objects, since this shared field is nos being properly stored and returns to its default value when deserializing.
I suppose this is the expected behaviour, so my question is... how can I make a shared field persistent?
I have read some comments to similar questions that say that this is a bad design, but it really makes sense (AFAIK) in my case, since this variable should be the same to all the object, but can be changed by the user and therefore should be stored.
Can you suggest another way of doing it?
Thanks!
EDIT: (sorry, I was in a hurry and couldn't complete my question until now)
My Class looks like this:
Public MustInherit Class NitrogenController
Private _active As Boolean
Private Shared _controlInterval As TimeSpan
Private _lastControlTime As Date
Public Property Active() As Boolean
Public Shared Property ControlInterval() As System.TimeSpan
'other properies that must be persisted
Public Function Control() As Boolean
If Not Now > _lastControlTime.Add(_controlInterval) Or Not _active Then
Return False
Else
DoControl()
_lastControlTime = Now
Return True
End If
End Function
End Class
So, the problem is that I can have several nitrogen controllers, but they should all have the same _controlInterval. That's the reason why I used a shared variable for this. But it does not preserve its value after serialization/deserialization. So... any ideas about how to do this?
Thanks!

Expose .NET DataTable properties to VBA via COM Interface

I am trying to create a .Net DLL basically as an abstraction layer for database connections; it is going to replace a current DLL we have that is written in VB6 and I am trying to match the current functionality as much as possible.
Anyway, the essential issue I am having is that I can't find a way to get .Net classes like DataColumnCollection or DataColumn to display in the VBA Interpreter -- It may say, for example, "Column" with the type "MarshalByValueComponent," but the value will be "No Variables".
I can get it to work if I completely re-create both classes (i.e. Fields as a collection of field, which inherits from DataColumn, and then define an interface for both), but that seems like a lot of added overhead for what (should be?) a pretty simple idea. I feel like I am just missing something very simple with the way the marshaller is handling the DataColumn class.
A lot of the stuff I am finding online is on how to convert a DataTable or DataReader to a legacy ADODB Recordset, but that also would add a lot of overhead... I'd rather leave it as a DataTable and create a COM interface to allow VBA to interact with it; that way if, for example, they want to write the table to an excel sheet, I wouldn't be duplicating work (convert to ADODB recordset, then read/write to excel sheet. You'd need to iterate the entire table twice...)
Sorry for the book-length explanation -- I felt the problem needed a bit of clarification since the root-cause is trying to match legacy functionality. Here is an example of my current interface that does not work:
Public Interface IDataTable
ReadOnly Property Column As DataColumn
End Interface
<ClassInterface(ClassInterfaceType.None)> _
<System.ComponentModel.DesignerCategory("")> _
<ComDefaultInterface(GetType(Recordset.IDataTable))> _
<Guid("E7AFBBB6-CB20-44EC-9CD2-BC70B94CD8B7")> _
Public Class Recordset : Inherits Data.DataTable : Implements IDataTable
Public ReadOnly Property Column As DataColumn Implements IDataTable.Column
Get
Return MyBase.Columns(0)
End Get
End Property
Note: I originally tried the property Columns as DataColumnCollection which returned MyBase.Columns. That came through as an Object, instead of MarshalByValueComponent, but was also empty. I know MyBase.Column(0) has a value, because I can put Msgbox(MyBase.Columns(0).ColumnName) right above the return in the get and it pops up fine (don't judge; this is way easier than using a debugger for this)...
I wouldn't mind just defining them both, but I can't inherit DataColumnCollection and the COM interface already sucks at dealing with generics. Is there any other way around this without re-inventing the wheel?
Thanks for your help!
I just spent the last 3 weeks doing something eerily similar.
I ended up making two .NET assemblies:
A pure .NET assembly that talks to the datastore (for use by .NET apps).
A "COM Interop" assembly that wraps the first assembly and adds the COM overhead (ADODB references and COM-Visible interfaces).
I call the second assembly from Excel VBA using the VSTO "AddIn.Object" property.
I ended up converting System.Data.DataTables to ADODB.Recordsets as you mentioned. Getting .NET and VBA talking about anything other than primitive types was beyond-frustrating for me. In fact, I ended up serializing some objects as JSON so the two worlds could communicate.
It does seem insane, but I reinvented the wheel.
I followed this MSDN article to make my .NET code callable by VBA.
I used this Code Project article (I'm sure you've seen) to convert to Recordset*.
I let the frameworks handle string, integers, etc.
For all other data types I used Json.Net and a custom VBA class to do JSON serialization.
*Converted article to VB.Net and added some extra error handling.
Okay, this probably isn't the most elegant (or complete, at this point) solution; but I think it's the route I am going to go.
Instead of converting the whole thing to an ADODB Recordset (and duplicating any iterations), I just threw out the DataTable class entirely and wrote my own Recordset class as a COM Wrapper for the a generic Data Reader (via the IDataReader interface) and added a new Field class to manage the type conversion and set up Fields as an array of Field (since interop hates generics)
It basically creates a forward-only ADODB Recordset (same limitations) but has the benefit of only loading one row at a time, so the bulk of the data can be handled as managed code until you know what they want to do with it (I'm going to add methods for ToArray, ToAccessDB, ToFile, etc that use the reader) while still allowing the ability to iterate through the Recordset from excel/access/vbscript/vb6 (if that's really what they want to do.. mostly needed that for legacy support anyway)
Here is an example, in case anyone else has to do this again; somewhat modified for brevity:
Public Interface IRecordset
ReadOnly Property CursorPosition As Integer
ReadOnly Property FieldCount As Integer
ReadOnly Property Fields As Field()
Function ReadNext() As Boolean
Sub Close()
End Interface
<System.ComponentModel.DesignerCategory("")> _
<ClassInterface(ClassInterfaceType.None)> _
<ComDefaultInterface(GetType(IRecordset))> _
<Guid("E7AFBBB6-CB20-44EC-9CD2-BC70B94CD8B7")> _
Public Class Recordset : Implements IRecordset : Implements IDisposable
Private _Reader = Nothing
Private _FieldCount As Integer = Nothing
Private _Fields() As Field
Public ReadOnly Property CursorPosition As Integer Implements IRecordset.CursorPosition...
Public ReadOnly Property FieldCount As Integer Implements IRecordset.FieldCount...
Public ReadOnly Property Fields As Field() Implements IRecordset.Fields...
Friend Sub Load(ByVal reader As IDataReader)
_Reader = reader
_FieldCount = _Reader.FieldCount
_Fields = Array.CreateInstance(GetType(DataColumn), _FieldCount)
For i = 0 To _FieldCount - 1
_Fields(i) = New Field(i, Me)
Next
End Sub
'This logic kinda sucks and is dumb.
Public Function ReadNext() As Boolean Implements IRecordset.ReadNext
_EOF = Not _Reader.Read()
If _EOF Then Return False
_CursorPosition += 1
For i = 0 To _FieldCount - 1
_Fields(i)._Value = _Reader.GetValue(i).ToString
Next
Return True
End Function
From here you just need to define some type like Field or Column and add an interop wrapper for that type:
Public Interface IField
ReadOnly Property Name As String
ReadOnly Property Type As String
ReadOnly Property Value As Object
End Interface
<System.ComponentModel.DesignerCategory("")> _
<ClassInterface(ClassInterfaceType.None)> _
<Guid("6230C670-ED0A-48D2-9429-84820DC2BE6C")> _
<ComDefaultInterface(GetType(IField))> _
Public Class Field : Implements IField
Private Reader As IDataReader = Nothing
Private Index As Integer = Nothing
Public ReadOnly Property Name As String Implements IField.Name
Get
Return Reader.GetName(Index)
End Get
End Property
Public ReadOnly Property Value As Object Implements IField.Value
Get
Return Reader.GetValue(Index)
End Get
End Property
Public ReadOnly Property Type As String Implements IField.Type
Get
Return Reader.GetDataTypeName(Index).ToString
End Get
End Property
Sub New(ByVal i As Integer, ByRef r As IDataReader)
Reader = r
Index = i
End Sub
End Class
All of this is rather silly, but it seems to work well.
Note: I've only been using .Net for about 4 days now, so this might be terrible, please feel free to comment on anything extremely stupid I might be doing.

Does instantiating and initializing work only when tied to button click?

Beginner question. How come I can do this:
Public Class Form1
Private StudentsInMyRoom As New ArrayList
Public Class student
Public name As String
Public courses As ArrayList
End Class
Private Sub btnCreateStudent_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCreateStudent.Click
Dim objStudent As New student
objStudent.name = "Ivan"
objStudent.courses = New ArrayList
StudentsInMyRoom.Add(objStudent)
End Sub
End Class
But I CANNOT do this:
Public Class Form1
Private StudentsInMyRoom As New ArrayList
Public Class student
Public name As String
Public courses As ArrayList
End Class
Dim objStudent As New student
objStudent.name = "Ivan"
objStudent.courses = New ArrayList
StudentsInMyRoom.Add(objStudent)
End Class
In the second example, all of the objStudent.etc get squiggly underlined and it says "declaration expected" when I hover over it. It's the same code except now it is not tied to clicking a button. Can't figure out what is the difference.
It's because the implementation needs to be in a method, the way you have it means the code couldn't possibly be executed, how would you reference this code from elsewhere?
It doesn't have to be tied to a click however:
Private Sub AnyNameYouLike
Dim objStudent As New student
objStudent.name = "Ivan"
objStudent.courses = New ArrayList
StudentsInMyRoom.Add(objStudent)
End Sub
Will work.
Rather than tell you how to fix this code directly, I'm going to explain what I think is going wrong with your thought process, so you can also do a better job writing code in the future.
What I see here is a simple misunderstanding for someone new to programming of how classes work. When you build and define a class, you are not (yet) allocating any memory in the computer, and you are not yet telling the computer to do anything. All you are doing is telling the computer about how an object might look at some point in the future. It's not until you actually create an instance of that class that anything happens:
Public Class MyClass
Public MyField As String
End Class
'Nothing has happened yet
Public myInstance As New MyClass()
'Now finally we have something we can work with,
' but we still haven't done anything
myInstance.MyField = "Hello World"
'It's only after this last line that we put a string into memory
Classes can only hold a few specific kinds of things: Fields, Properties, Delegates (events), and Methods (Subs and Functions). All of these things in the class are declarations of something, rather than the thing itself.
Looking at your samples, the code from your second example belongs inside of a method.
If you want this code to run every time you work with a new instance of your class, then there is a special method, called a constructor, that you can use. That is declared like this:
Public Class MyClass
Public MyField As String
'This is a constructor
Public Sub New()
MyField = "Hello World"
End Sub
End Class
However, even after this last example you still haven't told the computer to do any work. Again, you must create an instance of the class before the code in that constructor will run.
This is true of all code in all .Net programs anywhere. The way your program starts out is that the .Net framework creates an instance of a special object or form and then calls (runs) a specific method in that form to sort of get the ball rolling for your program. Everything else comes from there.
Eventually you will also learn about Shared items and Modules, that can (sort of) break this rule, in that you don't have to create an instance of the object before using it. But until you are comfortable working with instances, you should not worry about that too much.
Finally, I want to point out two practices in your code that professional programmers would consider poor practice. The first is ArrayLists. I can forgive this, because I suspect you are following a course of study that just hasn't covered generics yet. I only bring it up so you can know not to get too attached to them: there is something better coming. The second is your "obj" prefix. This was considered good practice once upon a time, but is no longer fashionable and now thought to be harmful to the readability of your code. You should not use these prefixes.

Is it bad practice to access a data member directly?

I recall being told by a professor the following is bad practice. But it makes stepping through code a lot less tedious. I'm just solicting comments on pros and cons:
Friend Class MyClass
Private isEmpty As Boolean
Public Property IsEmpty() As Boolean
Get
Return isEmpty
End Get
Set(ByVal Value As Integer)
isEmpty = value
End Set
End Property
Public Sub MyMethod()
''//Is this more correct:
If Me.IsEmpty() Then
''//Do something.
End If
''//Is this bad practice?:
If isEmpty Then
''//Do something.
End If
End Sub
End Class
If you have set up a property to access the private member variable "isEmpty", than yes, I would use the property inside of the class itself unless there is a darn good reason for not doing so.
The reason being that you may at a later point in time need to do more work when the property is set (or possibly when retrieved), and then you would likely have to find all the references to the private member variable in your class and change them to access the property instead.
To add to Ed's answer, some IDE's allow you to set a flag saying you do not want to dive into property getter/setters like this. I don't know about VB but C#/VS2008 will do this. So that takes 'stepping thru the code' convenience out of the equation in deciding what is the right thing to do.
It really gets down it instinct and experience as a developer. If you're super positive you'll never need to do any processing on that property, then I say access it directly. Otherwise write the wrapper, it doesn't really hurt.
Although, it has never been that big a deal to add it later either. I wouldn't get too religious about either method.

VB.NET, templates, reflection, inheritance, feeling adrift

I've just made myself up a problem and am now wondering how to solve it.
To begin with, I'm using some third-party components, including some calendar controls like schedule and timeline. They're used in the project classes more or less like that:
Friend Class TimeBasedDataView
'some members
End Class
Friend Class ScheduleDataView
Inherits TimeBasedDataView
Public Schedule As Controls.Schedule.Schedule
'and others
End Class
Friend Class TimeLineDataView
Inherits TimeBasedDataView
Public TimeLine As Controls.TimeLine.TimeLine
'and others
End Class
(Hmm, code coloring fail, never mind...) Now, to allow managing the look of data being presented there are some mechanisms including so called Style Managers. A lot of code in them repeats, varying almost only with the control it maintains:
Friend Class TimeLineStyleManager
Private m_TimeLine As TimeLineDataView
Private Sub Whatever()
m_TimeLine.TimeLine.SomeProperty = SomeValue
End Sub
End Class
Friend Class ScheduleStyleManager
Private m_Schedule As ScheduleDataView
Private Sub Whatever()
m_Schedule.Schedule.SomeProperty = SomeValue
End Sub
End Class
I was wondering if I could create some base class for those managers, like
Friend Class TimeBasedCtrlStyleManagerBase(Of T As TimeBasedDataView)
Private m_Control As T
'and others
End Class
which would unify these two, but I've got lost when it came to maintaining two components that have nothing in common (except their properties' names, etc.). Type reflection maybe? I'll be grateful for any advice ;)
Looks like you've got a case where you're looking to introduce inheritance, where it's not needed - it would've been better if the third party controls all adhered to a common interface (and then generics may have saved the day), but as they're third party, I'd assume that you have minimal impact on future direction of it.
You've done exactly the right kind of thing - inheritance and generics is perfect for this situation - but I would make the TimeBasedCtrlStyleManagerBase class MustInherit/abstract and then simply inherit down your two specific managers.
All common management code goes in the abstract base class and any specific code goes in the two specific managers.
Of course you'll need to change Private m_Control As T to Protected m_Control As T for this to work.
Friend Class TimeLineStyleManager
Inherits TimeBasedCtrlStyleManagerBase(Of TimeLineDataView)
End Class
Friend Class ScheduleStyleManager
Inherits TimeBasedCtrlStyleManagerBase(Of ScheduleDataView)
End Class