The existing Model diagram.
This is an OOP Areas and Volumes Calculator. It consists of a core containing ten classes representing different geometric shapes. These shapes fall into two categories - 2D and 3D. All of the 2D Shape Classes implement the Shape2D Interface. All of the 3D Classes extend one of the 2D Classes, therefore, they indirectly implement the Shape2D Interface. Additionally, all of the 3D Classes implement the Shape3D Interface.
Implementing an Interface in a Class means that your Class must use the methods and functions stipulated in the Interface. It also means that any of the ten Core Shape Classes can be created as an Object of type Shape2D, and the seven 3D Core Shape Classes can be created as an Object of type Shape3D.
Public Interface Shape2D Function getArea() As Decimal End Interface
Public Interface Shape3D Function getVolume() As Decimal End Interface
''' <summary> ''' 2D Circle class ''' </summary> Public Class Circle Implements Shape2D Public Const sortIndex As Integer = 0 Public Property Radius As Decimal Public Sub New(Radius As Decimal) Me.Radius = Radius End Sub ''' <summary> ''' Implemented getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overridable Function getArea() As Decimal Implements Shape2D.getArea Return CDec(Math.PI * Me.Radius ^ 2) End Function End Class
''' <summary> ''' 2D Rectangle class ''' </summary> Public Class Rectangle Implements Shape2D Public Const sortIndex As Integer = 1 Public Property Length As Decimal Public Property Width As Decimal Public Sub New(Length As Decimal, Width As Decimal) Me.Length = Length Me.Width = Width End Sub ''' <summary> ''' Implemented getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overridable Function getArea() As Decimal Implements Shape2D.getArea Return Me.Length * Me.Width End Function End Class
''' <summary> ''' 2D Triangle class ''' </summary> Public Class Triangle Implements Shape2D Public Const sortIndex As Integer = 2 Public Property SideA As Decimal Public Property SideB As Decimal Public Property SideC As Decimal Public Sub New(SideA As Decimal, SideB As Decimal, SideC As Decimal) Me.SideA = SideA Me.SideB = SideB Me.SideC = SideC End Sub ''' <summary> ''' Implemented getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overridable Function getArea() As Decimal Implements Shape2D.getArea Try Dim s As Decimal = CDec((Me.SideA / 2) + (Me.SideB / 2) + (Me.SideC / 2)) Return CDec(Math.Sqrt(s * ((s - Me.SideA) * (s - Me.SideB) * (s - Me.SideC)))) Catch ex As exception Return 0D End Try End Function End Class
''' <summary> ''' 3D Cone class ''' </summary> Public Class Cone Inherits Circle Implements Shape3D Public Shadows Const sortIndex As Integer = 3 Public Property Height As Decimal Public Sub New(Height As Decimal, Radius As Decimal) MyBase.New(Radius) Me.Height = Height End Sub ''' <summary> ''' Overriden getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overrides Function getArea() As Decimal Return CDec(Math.PI * MyBase.Radius * (MyBase.Radius + Math.Sqrt(Me.Height ^ 2 + MyBase.Radius ^ 2))) End Function ''' <summary> ''' Implemented getVolume function ''' </summary> ''' <returns>Volume of shape with specified dimensions</returns> Public Function getVolume() As Decimal Implements Shape3D.getVolume Return CDec((Math.PI * MyBase.Radius ^ 2 * Me.Height) / 3) End Function End Class
''' <summary> ''' 3D Cylinder class ''' </summary> Public Class Cylinder Inherits Circle Implements Shape3D Public Shadows Const sortIndex As Integer = 4 Public Property Height As Decimal Public Sub New(Height As Decimal, Radius As Decimal) MyBase.New(Radius) Me.Height = Height End Sub ''' <summary> ''' Overriden getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overrides Function getArea() As Decimal Return CDec(2 * Math.PI * MyBase.Radius * Me.Height + 2 * Math.PI * MyBase.Radius ^ 2) End Function ''' <summary> ''' Implemented getVolume function ''' </summary> ''' <returns>Volume of shape with specified dimensions</returns> Public Function getVolume() As Decimal Implements Shape3D.getVolume Return CDec(Math.PI * MyBase.Radius ^ 2 * Me.Height) End Function End Class
''' <summary> ''' 3D Sphere class ''' </summary> Public Class Sphere Inherits Circle Implements Shape3D Public Shadows Const sortIndex As Integer = 5 Public Sub New(Radius As Decimal) MyBase.New(Radius) End Sub ''' <summary> ''' Overriden getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overrides Function getArea() As Decimal Return CDec(4 * Math.PI * MyBase.Radius ^ 2) End Function ''' <summary> ''' Implemented getVolume function ''' </summary> ''' <returns>Volume of shape with specified dimensions</returns> Public Function getVolume() As Decimal Implements Shape3D.getVolume Return CDec((Math.PI * MyBase.Radius ^ 3 * 4) / 3) End Function End Class
''' <summary> ''' 3D Cuboid class ''' </summary> Public Class Cuboid Inherits Rectangle Implements Shape3D Public Shadows Const sortIndex As Integer = 6 Public Property Height As Decimal Public Sub New(Height As Decimal, Length As Decimal, Width As Decimal) MyBase.New(Length, Width) Me.Height = Height End Sub ''' <summary> ''' Overriden getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overrides Function getArea() As Decimal Return CDec(MyBase.getArea() * 2 + MyBase.Length * Me.Height * 2 + MyBase.Width * Me.Height * 2) End Function ''' <summary> ''' Implemented getVolume function ''' </summary> ''' <returns>Volume of shape with specified dimensions</returns> Public Function getVolume() As Decimal Implements Shape3D.getVolume Return CDec(MyBase.getArea() * Me.Height) End Function End Class
''' <summary> ''' 3D Prism class ''' </summary> Public Class Prism Inherits Triangle Implements Shape3D Public Shadows Const sortIndex As Integer = 7 Public Property Length As Decimal Public Sub New(Length As Decimal, SideA As Decimal, SideB As Decimal, SideC As Decimal) MyBase.New(SideA, SideB, SideC) Me.Length = Length End Sub ''' <summary> ''' Overriden getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overrides Function getArea() As Decimal Return CDec(MyBase.getArea() * 2 + MyBase.SideA * Me.Length + MyBase.SideB * Me.Length + MyBase.SideC * Me.Length) End Function ''' <summary> ''' Implemented getVolume function ''' </summary> ''' <returns>Volume of shape with specified dimensions</returns> Public Function getVolume() As Decimal Implements Shape3D.getVolume Return CDec(MyBase.getArea() * Me.Length) End Function End Class
''' <summary> ''' 3D 3 sided Pyramid class ''' </summary> Public Class Three_Sided_Pyramid Inherits Triangle Implements Shape3D Public Shadows Const sortIndex As Integer = 8 Public Const tagNumber As Integer = 3 Public Property Height As Decimal Public Property Base As Decimal Public Sub New(Height As Decimal, Base As Decimal, Sidelength As Decimal) MyBase.New(Base, Sidelength, Sidelength) Me.Base = Base Me.Height = Height End Sub ''' <summary> ''' Overriden getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overrides Function getArea() As Decimal Return CDec(getBaseArea(MyBase.SideA) + MyBase.getArea() * 3) End Function ''' <summary> ''' Implemented getVolume function ''' </summary> ''' <returns>Volume of shape with specified dimensions</returns> Public Function getVolume() As Decimal Implements Shape3D.getVolume Return CDec((getBaseArea(MyBase.SideA) * Me.Height) / 3) End Function ''' <summary> ''' getBaseArea helper function ''' </summary> ''' <param name="b"></param> ''' <returns>3 sided pyramid base area</returns> Private Function getBaseArea(b As Decimal) As Decimal Dim s As Decimal = CDec((b * 3) / 2) Return CDec(Math.Sqrt(s * ((s - b) * (s - b) * (s - b)))) End Function End Class
''' <summary> ''' 3D 4 sided Pyramid class ''' </summary> Public Class Four_Sided_Pyramid Inherits Triangle Implements Shape3D Public Shadows Const sortIndex As Integer = 9 Public Const tagNumber As Integer = 4 Public Property Height As Decimal Public Property Base As Decimal Public Sub New(Height As Decimal, Base As Decimal, Sidelength As Decimal) MyBase.New(Base, Sidelength, Sidelength) Me.Base = Base Me.Height = Height End Sub ''' <summary> ''' Overriden getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overrides Function getArea() As Decimal Return CDec(MyBase.SideA ^ 2 + 2 * MyBase.SideA * Math.Sqrt(Me.Height ^ 2 + MyBase.SideA ^ 2 / 4)) End Function ''' <summary> ''' Implemented getVolume function ''' </summary> ''' <returns>Volume of shape with specified dimensions</returns> Public Function getVolume() As Decimal Implements Shape3D.getVolume Return CDec(((MyBase.SideA ^ 2) * Me.Height) / 3) End Function End Class
User input in the Form is passed to the Coordinating Class, which invokes the ClassFactory getClass function, which creates a new instance of the correct Class by substitution as an instance of Shape2D. The differences between the classes are shown as prompts in the Controller. The values used are as directed by user input. After calculating the Surface Area via the Shape2D substitution, an attempt is made to cast the instance of Shape2D to an instance of Shape3D. If the cast succeeds, a 3D shape object is selected, if the cast fails, it was a 2D shape object selected. The results of the area calculations and possibly also the volume calculations are displayed in the View Label at the bottom of the Form. The grid control used to elicit values doubles as a part of the View.
Public Class Form1 Dim gridValues As New Dictionary(Of String, Arguments) ''' <summary> ''' Sets up the Dictionary, DGV and sets the ComboBox SelectedIndex ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load gridValues = Aggregator.getConcreteClassDetails(New String() {"Three_Sided_Pyramid", "Four_Sided_Pyramid"}, New Integer() {2, 2}) ComboBox1.DataSource = gridValues.Keys.ToArray ComboBox1.SelectedIndex = 0 DataGridView1.Columns(0).ReadOnly = True End Sub ''' <summary> ''' Changes the DGV prompts ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged If ComboBox1.SelectedIndex = -1 Then Return DataGridView1.Rows.Clear() For Each s As String In gridValues(ComboBox1.Text).ArrayArgs DataGridView1.Rows.Add(s) Next If DataGridView1.Rows.Count < 4 Then DataGridView1.Rows.Add(4 - DataGridView1.Rows.Count) End If For x As Integer = 0 To DataGridView1.Rows.Count - 1 If DataGridView1(0, x).Value Is Nothing Then DataGridView1(1, x).ReadOnly = True End If Next Label1.Text = "" DataGridView1.Focus() End Sub ''' <summary> ''' Initiates the calculation ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim cellValues(DataGridView1.Rows.Count - 1, 1) As Object For x As Integer = 0 To DataGridView1.Rows.Count - 1 cellValues(x, 0) = DataGridView1(0, x).Value cellValues(x, 1) = DataGridView1(1, x).Value Next Label1.Text = Coordinator.getValues(ComboBox1.Text, cellValues, gridValues(ComboBox1.Text).Tag) End Sub End Class
To add a concrete class to the Model, a class must be added that either implements Shape2D (for a two dimensional shape), or inherits a Shape2D class and implements Shape3D. The class must implement the methods described in the relevant interface.
To add a 3D shape to the Model (in this case an Icosahedron)...
''' <summary> ''' 3D Icosahedron class ''' </summary> Public Class Icosahedron Inherits Triangle Implements Shape3D End Class
Add the Overridden and Implemented methods
''' <summary> ''' 3D Icosahedron class ''' </summary> Public Class Icosahedron Inherits Triangle Implements Shape3D ''' <summary> ''' Overriden getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overrides Function getArea() As Decimal End Function ''' <summary> ''' Implemented getVolume function ''' </summary> ''' <returns>Volume of shape with specified dimensions</returns> Public Function getVolume() As Decimal Implements Shape3D.getVolume Throw New NotImplementedException() End Function End Class
Add the Constructor and a Property, and fill out the area and volume stubs
''' <summary> ''' 3D Icosahedron class ''' </summary> Public Class Icosahedron Inherits Triangle Implements Shape3D Public Shadows Const sortIndex As Integer = 10 Public Property SideLength As Decimal Public Sub New(SideLength As Decimal) MyBase.New(SideLength, SideLength, SideLength) Me.SideLength = SideLength End Sub ''' <summary> ''' Overriden getArea function ''' </summary> ''' <returns>Surface area of shape with specified dimensions</returns> Public Overrides Function getArea() As Decimal Return MyBase.getArea * 20 End Function ''' <summary> ''' Implemented getVolume function ''' </summary> ''' <returns>Volume of shape with specified dimensions</returns> Public Function getVolume() As Decimal Implements Shape3D.getVolume Return CDec(((15 + 5 * Math.Sqrt(5)) / 12) * SideLength ^ 3) End Function End Class
The Constant sortIndex is used in sorting the concrete classes. And that's all that is involved in extending the Model.
The extended Model diagram.
This example demonstrates OOP, Substitution, and Reflection. An OOP application uses pre-defined Objects (The Model's Concrete Shape Classes). Substitution is where you can Cast any of those concrete Shape Classes as an Object of type Shape2D, and some as Shape3D. Reflection is used on app. startup to gather information about the extensible model, then again in the ClassFactory Class to create an instance of a Class from a String name.
This example is available for download here...