Beginner VBA programmer here (and beginner to programming in general) looking to learn more about how effective OOP is done.
Can someone explain or provide a reference discussing the benefits/purpose of using - INSIDE of a class module - Private Property Get and/or Let/Set statements in VBA vs accessing properties directly although no manipulation is required of the data?
Example:
I have created a cDimension class (in Excel). This class draws lines as Shape objects, and a few other things that are not relevant. The DrawingScale variable allows the entire drawing to be scaled as desired. Some of the properties require manipulation when Getting/Setting, others don't.
So, for example, pWidth needs to be scaled going in:
'clsDimension
Private pWidth As Single
Private Property Get Width() As Single
Width = pWidth
End Property
Private Property Let Width(w As Single)
pWidth = w / DrawingScale
End Property
But pColor does not require any manipulation, in or out:
Private pColor As Integer
Private Property Get Color() As Integer
Color = pColor
End Property
Private Property Let Color(c As Integer)
pColor = c
End Property
The pWidth property is an instance where using the Private Property Get and Let methods for procedures inside of the class itself makes sense to me. However, my question is: is there any reason to also use Private Property methods to Get and Let/Set the pColor property as well, as I have given them above?
Public Function Line(sht As Worksheet, L As tLine, Optional c = vbBlack) As Shape
Width = DistanceBetweenTwoPoints(L.first.x, L.first.y, _
L.second.x, L.second.y) '<-- pWidth is scaled
Color = c '<-- Vs. just using pColor = c
Set Line = sht.Shapes.AddLine(L.first.x, L.first.y, L.second.x, L.second.y)
End Function
Thanks in advance.
If you are not manipulating the values on the way in or way out, just use public variables. VBA is more like Python than Java or C++ in that there is no real penalty for switching from public variables to "getter/setter" functions down the road.
Let's say you start with the following code:
'clsCar
Public Speed As Double
Public Sub Drive()
MsgBox "Driving at " & Speed & " MPH"
End Sub
'Module1
Sub DriveCar()
Set Car = New clsCar
Car.Speed = 100
Car.Drive 'shows msg: "Driving at 100 MPH"
End Sub
Then you decide that driving that fast is dangerous, so you want to add a "governor" to your vehicle and need to update your class:
'clsCar
Private mSpeed As Double
Private Const SpeedLimit As Double = 55
Public Property Get Speed()
Speed = mSpeed
End Property
Public Property Let Speed(Val As Double)
If Val > SpeedLimit Then
mSpeed = SpeedLimit
ElseIf Val < 0 Then
mSpeed = 0
Else
mSpeed = Val
End If
End Property
Public Sub Drive()
MsgBox "Driving at " & Speed & " MPH"
End Sub
'Module1
'Note that no changes to this code are necessary; the change
' is entirely encapsulated within the class (as it should be)
Sub DriveCar()
Set Car = New clsCar
Car.Speed = 100
Car.Drive 'shows msg: "Driving at 55 MPH"
End Sub
Tim Williams's comment is what you will often hear as justification for unnecessarily using Get/Let/Set in VBA, but that's the byproduct of good advice from other languages (C++ and Java, notably) being misapplied to VBA/VB6.
As always.... it depends. Your x property should definitely be accessed via the letter because the letter performs a calculation on the input.
'clsDimension
Private Let Width(w As Single)
pWidth = w / DrawingScale
End Property
You should NOT access the pWidth variable directly. If you did, you would have to duplicate the pWidth = w / DrawingScale logic elsewhere in your module.
Your pColor property could be a public variable, because there's no logic in getting or setting it. I don't recommend it though. What if later you realize you don't want to allow certain colors? Then you would need logic behind it and you would need to switch to a property anyway. The property is easier to maintain and more semantically correct.
The value I see is reduced code, which you are not taking advantage of in your sample code. You could replace this:
'clsDimension
Private pWidth As Single
Private Property Get Width() As Single
Width = pWidth
End Property
Private Property Let Width(w As Single)
pWidth = w / DrawingScale
End Property
with this:
'clsDimension
Private Property Get Width() As Single
Width = w / DrawingScale
End Property
This eliminates the potential ambiguity of accessing pWidth or the width property since pWidth no longer exists. It also eliminates the potential pitfall of Width being read before it has been written, although it would be wise to add a bit of code that checks that w and DrawingScale have been set to valid values before you read Width.
Related
I am trying to create a receipt form where people will confirm if they've received the full quantity of an order. As part of this, I want the following to happen:
If they received the full quantity, a green check mark appears
If they received a partial quantity, an orange triangle appears
If they received no items, a red x appears
To accomplish this, I'm using a continuous form with 3 image files for each situation. I'm using the code below to change the image when the quantity is changed. The problem is, when the quantity is change on 1 line, the symbol changes for all lines. I'll post pictures as well.
Any thoughts on how I can fix this?
I'm open to other methods of accomplishing this idea too.
Private Sub FinalQTY_AfterUpdate()
If IsNull(Me.FinalQty) Then
MsgBox "You must enter a quantity for this item"
Me.FinalQty.SetFocus
Exit Sub
Else
LValue = Me.[FinalQty]
If IsNumeric(LValue) = 0 Then
Me.FinalQty = ""
MsgBox "Qty must be a numeric value"
Me.QTY.SetFocus
Exit Sub
End If
End If
Me.FinalTotalPrice = Me.FinalPrice * Me.FinalQty
If Me.FinalQty = 0 Then
Me.Yes.Visible = False
Me.Change.Visible = False
Me.No.Visible = True
End If
If Me.FinalQty < Me.QTY Then
Me.Yes.Visible = False
Me.Change.Visible = True
Me.No.Visible = False
End If
If Me.FinalQty = Me.QTY Then
Me.Yes.Visible = True
Me.Change.Visible = False
Me.No.Visible = False
End If
End Sub
This is before I adjust the quantity:
This is after I adjust the qty of only the second line:
Since the formatting of each record displayed by a continuous form is inherited from the form design template, any changes to the template will be automatically applied to all records displayed by the form, aside from Conditional Formatting rules in effect or the handful of properties which may changed via the OnPaint event of the Detail section.
One possible alternative might be to add a new field to your table with a data type of OLE Object and populate the value on the AfterUpdate event using the AppendChunk method, sourcing image data from a separate table containing three records corresponding to your green tick, orange triangle, and red cross images.
I have this code that uses a for loop to iterate through every pixel of an image and then based on a user choice changes the value of the red, green or blue channel.
I am using a switch statement to select the channel and the iterate through every pixel in a for loop for that pixels appropriate color property. It feels very cludgy but I can't think of a way to simplify it.
Is there another way to access the property so I can eliminate the switch statement and just have one for loop that select the right pixel color based on user choice?
func applyFilterTo(image:UIImage,forColor:String, withIntensity:Int) -> RGBAImage {
var myRGBA = RGBAImage(image:image)!
switch forColor {
case "RED":
for x in 0..<myRGBA.width {
for y in 0..<myRGBA.height {
let pixelIndex = y * myRGBA.width + x
var pixel = myRGBA.pixels[pixelIndex]
let newValue = Double(pixel.red) + (Double(withIntensity)/100)*Double(pixel.red)
myRGBA.pixels[pixelIndex].red = UInt8(max(0,min(255,newValue)))
}
}
...
}
the above repeats for other options but the only thing that changes is the property I choose (red, blue, green). Better way to do this?
A good way to do this is replacing switch statements with multiple dispatch, such as the Visitor Pattern. Depending on how many options you will have in the switch statement, the switch statement is the easiest to read and understand.
Can I create a reference to a handle object in a way that I can replace the object itself at one point and the references will be updated?
Example:
classdef IShifter < handle
methods (Abstract)
x = Shift(this, x);
end
end
classdef Shifter1 < IShifter
methods
function x = Shift(this, x)
x = circshift(x, 1);
end
end
end
classdef Shifter2 < IShifter
methods
function x = Shift(this, x)
x = [ 0 ; x ];
end
end
end
classdef Item
properties (Access = 'private')
shifter; % should be a pointer/reference to the object which is in the respective parent container object
end
methods
function this = Item(shifter)
this.shifter = shifter;
end
function x = test(this, x)
x = this.shifter.Shift(x);
end
end
end
% note this is a value class, NOT a handle class!
classdef ItemContainer
properties
shifter;
items;
end
methods
function this = ItemContainer()
this.shifter = Shifter1;
this.items{1} = Item(this.shifter);
this.items{2} = Item(this.shifter);
end
function Test(this)
this.items{1}.Test( [ 1 2 3] )
this.items{2}.Test( [ 1 2 3] )
end
end
end
Then, the output should be:
items = ItemContainer();
items.Test();
[ 3 1 2 ]
[ 3 1 2 ]
items.shifter = Shifter2;
items.Test();
[ 0 1 2 ]
[ 0 1 2 ]
But instead it is:
items = ItemContainer();
items.Test();
[ 3 1 2 ]
[ 3 1 2 ]
items.shifter = Shifter2;
items.Test();
[ 3 1 2 ]
[ 3 1 2 ]
because assigning a new Shifter object to parent object items does not update the references in the containers.
I am looking for something like in C where all the "shifter" properties are pointers and I can put whatever Shifter object I want into this "address".
ItemContainer and Item are not handle classes.
I want to avoid using events to update the references or implementing a set method to update the references
The pass-by-reference concept in Matlab only goes so far (and is mostly limited to handle-classes anyway). It does not go as far as you'd like it to go.
If you don't want to use a set method, you can make items a dependent property with a get method that returns {this.shifter,this.shifter}
According to your comments, items is a bit more complicated than just a cell array. Thus, you may want to set up your object with a property itemsStore (or itemsCache, or whatever name you prefer for something that temporarily stores items), and a dependent property items. The get method to items checks whether itemsStore is empty; if yes, it reconstructs the item array and stores it in itemsStore, if not, it just returns the contents of itemStore. Additionally, you need to add a set method to shifter that empties itemsStore, so that items needs to be recreated. Note that MLint will give you a warning that a set method for a property shouldn't write into another property. This warning is meant to tell you that when you have saved an object and then load it again from disk, all the set methods will be executed. Depending on the order of execution, set methods that write into other properties may create unexpected results. In your case, it's no problem because an emptied itemStore is something that your code is designed to handle. If you want, you can right-click on the MLint warning, disabling it for that line.
Unfortunately I don't think this is possible in Matlab.
However, you can use a set method or subsasgn to redefine the content of items{1} and items{2} when item is defined. Then you will be able to get the behavior you're asking for.
Best,
how can I get the value of a given netlogo patches-own. I can only get the patches-own names (with .word.program.patchesOwn()) but I don't know how to get their values.
thanks
You want the values from all of the patches, or the value from a particular patch, or what?
I'll suppose you want the value from a particular patch.
Assuming, to begin with:
import org.nlogo.headless.HeadlessWorkspace;
HeadlessWorkspace workspace = HeadlessWorkspace.newInstance();
workspace.open("models/Sample Models/Biology/Ants.nlogo");
workspace.command("setup");
Then you don't need anything other than HeadlessWorkspace.report to retrieve a value from a patch, so e.g.:
double food = ((Double) workspace.report("[food] of patch -17 -19")).doubleValue();
Another, more cumbersome solution path involves accessing engine data structures directly:
Patch p = workspace.world().getPatchAt(-17, -19);
int foodIndex = workspace.world().program().patchesOwn().indexOf("FOOD");
double food = ((Double) p.getPatchVariable(foodIndex)).doubleValue();
I'm trying to write an object oriented program (as a learning exercise, I know there may be simpler ways to do it) in which beads bounce around a 2D plane bounded by a ring. Each bead is an object defined by a class ball. In setting the initial positions of the balls I need to check that no other ball has already been placed at the same x and y coordinates.
#Class for the beads
class ball:
NumbBalls = 0
#Constructor
def __init__(self,Beads):
self.ball = sphere(pos = vector(0,0,0), radius = radiusBall,color=color.red)
ball.NumbBalls += 1
self.ball.pos = self.createInitialPosition(Beads)
#Assign ball its initial position
def createInitialPosition(self,other):
#CODE to compare self.ball.pos and other.ball.pos which returns a unique position coordinate
#Main program
NumbBeads = 100
Beads = []#Create empty list for all the beads
#create balls in random initial positions
Beads = [ball(Beads) for item in range(NumbBeads)]
I can get this to work if I create two objects bead1 and bead2 and then pass bead1 and bead2 as arguments ie bead2 = ball(bead1) but how do I do this if I am generating a list of beads and want all of them to be compared with self. Hopefully that makes sense.
Perhaps rather than the approach you are currently taking, you should consider something along these lines (of course, with the necessary changes to your class definition/methods):
N = 100
initialPositions = []
while len(initialPositions) <= N:
newPosition = generateRandomPosition()
if newPosition in initialPositions:
pass
else:
initialPositions.append(newPosition)
ballList = [ ball(p) for p in initialPositions ]
In other words, generate a list of initial positions outside of the creation of your objects, and do whatever validation/restriction you need during that creation. Then just create your objects from that list. If N is really large, you might want to consider a dictionary (or a mutable set of some sort) instead of a list for initialPositions, to help speed up the membership testing, but it's still the same concept...