Constant With Dot Operator (VBA)











up vote
9
down vote

favorite












I want to have a catalog of constant materials so I can use code that looks like the following:



Dim MyDensity, MySymbol
MyDensity = ALUMINUM.Density
MySymbol = ALUMINUM.Symbol


Obviously the density and symbol for aluminum are not expected to change so I want these to be constants but I like the dot notation for simplicity.



I see a few options but I don't like them.





  1. Make constants for every property of every material. That seems like too many constants since I might have 20 materials each with 5 properties.



    Const ALUMINUM_DENSITY As Float = 169.34
    Const ALUMINUM_SYMBOL As String = "AL"



  2. Define an enum with all the materials and make functions that return the properties. It's not as obvious that density is constant since its value is returned by a function.



    Public Enum Material
    MAT_ALUMINUM
    MAT_COPPER
    End Enum

    Public Function GetDensity(Mat As Material)
    Select Case Mat
    Case MAT_ALUMINUM
    GetDensity = 164.34
    End Select
    End Function



It doesn't seem like Const Structs or Const Objects going to solve this but maybe I'm wrong (they may not even be allowed). Is there a better way?










share|improve this question




















  • 2




    This is attracting enough interest that it would be nice to see the final product posted as a question on Code Review if you're up for it.
    – Comintern
    Nov 16 at 18:31















up vote
9
down vote

favorite












I want to have a catalog of constant materials so I can use code that looks like the following:



Dim MyDensity, MySymbol
MyDensity = ALUMINUM.Density
MySymbol = ALUMINUM.Symbol


Obviously the density and symbol for aluminum are not expected to change so I want these to be constants but I like the dot notation for simplicity.



I see a few options but I don't like them.





  1. Make constants for every property of every material. That seems like too many constants since I might have 20 materials each with 5 properties.



    Const ALUMINUM_DENSITY As Float = 169.34
    Const ALUMINUM_SYMBOL As String = "AL"



  2. Define an enum with all the materials and make functions that return the properties. It's not as obvious that density is constant since its value is returned by a function.



    Public Enum Material
    MAT_ALUMINUM
    MAT_COPPER
    End Enum

    Public Function GetDensity(Mat As Material)
    Select Case Mat
    Case MAT_ALUMINUM
    GetDensity = 164.34
    End Select
    End Function



It doesn't seem like Const Structs or Const Objects going to solve this but maybe I'm wrong (they may not even be allowed). Is there a better way?










share|improve this question




















  • 2




    This is attracting enough interest that it would be nice to see the final product posted as a question on Code Review if you're up for it.
    – Comintern
    Nov 16 at 18:31













up vote
9
down vote

favorite









up vote
9
down vote

favorite











I want to have a catalog of constant materials so I can use code that looks like the following:



Dim MyDensity, MySymbol
MyDensity = ALUMINUM.Density
MySymbol = ALUMINUM.Symbol


Obviously the density and symbol for aluminum are not expected to change so I want these to be constants but I like the dot notation for simplicity.



I see a few options but I don't like them.





  1. Make constants for every property of every material. That seems like too many constants since I might have 20 materials each with 5 properties.



    Const ALUMINUM_DENSITY As Float = 169.34
    Const ALUMINUM_SYMBOL As String = "AL"



  2. Define an enum with all the materials and make functions that return the properties. It's not as obvious that density is constant since its value is returned by a function.



    Public Enum Material
    MAT_ALUMINUM
    MAT_COPPER
    End Enum

    Public Function GetDensity(Mat As Material)
    Select Case Mat
    Case MAT_ALUMINUM
    GetDensity = 164.34
    End Select
    End Function



It doesn't seem like Const Structs or Const Objects going to solve this but maybe I'm wrong (they may not even be allowed). Is there a better way?










share|improve this question















I want to have a catalog of constant materials so I can use code that looks like the following:



Dim MyDensity, MySymbol
MyDensity = ALUMINUM.Density
MySymbol = ALUMINUM.Symbol


Obviously the density and symbol for aluminum are not expected to change so I want these to be constants but I like the dot notation for simplicity.



I see a few options but I don't like them.





  1. Make constants for every property of every material. That seems like too many constants since I might have 20 materials each with 5 properties.



    Const ALUMINUM_DENSITY As Float = 169.34
    Const ALUMINUM_SYMBOL As String = "AL"



  2. Define an enum with all the materials and make functions that return the properties. It's not as obvious that density is constant since its value is returned by a function.



    Public Enum Material
    MAT_ALUMINUM
    MAT_COPPER
    End Enum

    Public Function GetDensity(Mat As Material)
    Select Case Mat
    Case MAT_ALUMINUM
    GetDensity = 164.34
    End Select
    End Function



It doesn't seem like Const Structs or Const Objects going to solve this but maybe I'm wrong (they may not even be allowed). Is there a better way?







vba const constants






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 16 at 17:19









Mathieu Guindon

39.6k761137




39.6k761137










asked Nov 16 at 17:11









R. Binter

483




483








  • 2




    This is attracting enough interest that it would be nice to see the final product posted as a question on Code Review if you're up for it.
    – Comintern
    Nov 16 at 18:31














  • 2




    This is attracting enough interest that it would be nice to see the final product posted as a question on Code Review if you're up for it.
    – Comintern
    Nov 16 at 18:31








2




2




This is attracting enough interest that it would be nice to see the final product posted as a question on Code Review if you're up for it.
– Comintern
Nov 16 at 18:31




This is attracting enough interest that it would be nice to see the final product posted as a question on Code Review if you're up for it.
– Comintern
Nov 16 at 18:31












5 Answers
5






active

oldest

votes

















up vote
12
down vote



accepted










Make VBA's equivalent to a "static class". Regular modules can have properties, and nothing says that they can't be read-only. I'd also wrap the density and symbol up in a type:



'Materials.bas

Public Type Material
Density As Double
Symbol As String
End Type

Public Property Get Aluminum() As Material
Dim output As Material
output.Density = 169.34
output.Symbol = "AL"
Aluminum = output
End Property

Public Property Get Iron() As Material
'... etc
End Property


This gets pretty close to your desired usage semantics:



Private Sub Example()
Debug.Print Materials.Aluminum.Density
Debug.Print Materials.Aluminum.Symbol
End Sub


If you're in the same project, you can even drop the explicit Materials qualifier (although I'd recommend making it explicit):



Private Sub Example()
Debug.Print Aluminum.Density
Debug.Print Aluminum.Symbol
End Sub





share|improve this answer

















  • 1




    I like that, although then code that does Materials.Aluminum.Density = 42 from outside will compile, even though will do nothing.
    – GSerg
    Nov 16 at 17:38










  • @GSerg - Yeah, that's one issue - the returned Material really isn't a constant - there's nothing to prevent the caller from grabbing a reference to it. Ideally I'd combine this with one of the solutions from this QA to make the return value immutable, but that's a bit above and beyond.
    – Comintern
    Nov 16 at 17:47










  • No, it's safe in the sense that the caller cannot mess it up, because Material is a value type. It's just that it allows non-sensible code to compile instead of giving an error of some sort.
    – GSerg
    Nov 16 at 17:49












  • I like that! it gives easy to read notation, its value can't be modified, it lets me even pass the result like a variable with an enforced type. Example: Function CalculateWeight(Mat As Material, Volume as float) End Function Weight = CalculateWeight(Materials.Aluminum, 50.6)
    – R. Binter
    Nov 16 at 17:49








  • 1




    @GSerg - Right, but you do need to be aware that you shouldn't Dim x As Material: x = Materials.Aluminum and then start messing around with it locally.
    – Comintern
    Nov 16 at 17:51


















up vote
5
down vote













IMO @Comintern hit the nail on the head; this answer is just another possible alternative.





Make an interface for it. Add a class module, call it IMaterial; that interface will formalize the get-only properties a Material needs:



Option Explicit
Public Property Get Symbol() As String
End Property

Public Property Get Density() As Single
End Property


Now bring up Notepad and paste this class header:



VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "StaticClass1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit


Save it as StaticClass1.cls and keep it in your "frequently needed VBA code files" folder (make one if you don't have one!).



Now add a prototype implementation to the text file:



VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "Material"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Implements IMaterial

Private Const mSymbol As String = ""
Private Const mDensity As Single = 0

Private Property Get IMaterial_Symbol() As String
IMaterial_Symbol = Symbol
End Property

Private Property Get IMaterial_Density() As Single
IMaterial_Density = Density
End Property

Public Property Get Symbol() As String
Symbol = mSymbol
End Property

Public Property Get Density() As Single
Density = mDensity
End Property


Save that text file as Material.cls.



Now import this Material class into your project; rename it to AluminiumMaterial, and fill in the blanks:



Private Const mSymbol As String = "AL"
Private Const mDensity As Single = 169.34


Import the Material class again, rename it to AnotherMaterial, fill in the blanks:



Private Const mSymbol As String = "XYZ"
Private Const mDensity As Single = 123.45


Rinse & repeat for every material: you only need to supply each value once per material.



If you're using Rubberduck, add a folder annotation to the template file:



'@Folder("Materials")


And then the Code Explorer will cleanly regroup all the IMaterial classes under a Materials folder.



Having "many modules" is only a problem in VBA because the VBE's Project Explorer makes it rather inconvenient (by stuffing every single class under a single "classes" folder). Rubberduck's Code Explorer won't make VBA have namespaces, but lets you organize your VBA project in a structured way regardless.



Usage-wise, you can now have polymorphic code written against the IMaterial interface:



Public Sub DoSomething(ByVal material As IMaterial)
Debug.Print material.Symbol, material.Density
End Sub


Or you can access the get-only properties from the exposed default instance (that you get from the modules' VB_PredeclaredId = True attribute):



Public Sub DoSomething()
Debug.Print AluminumMaterial.Symbol, AluminumMaterial.Density
End Sub


And you can pass the default instances around into any method that needs to work with an IMaterial:



Public Sub DoSomething()
PrintToDebugPane AluminumMaterial
End Sub

Private Sub PrintToDebugPane(ByVal material As IMaterial)
Debug.Print material.Symbol, material.Density
End Sub


Upsides, you get compile-time validation for everything; the types are impossible to misuse.



Downsides, you need many modules (classes), and if the interface needs to change that makes a lot of classes to update to keep the code compilable.






share|improve this answer





















  • That way we end up with a bunch of classes, and the OP has already disliked a bunch of modules.
    – GSerg
    Nov 16 at 17:52








  • 2




    @GSerg I know, and I believe this answer already states this clearly. Yet it's how you get 100% type safety and compiler-assisted consistency, without exposing a mutable UDT (which have frustrating limitations in VBA). It's a solution worth considering regardless of what the OP thinks of having many modules. Having many classes isn't a problem in any OOP language; my take is that it's only a problem in VBA because the IDE makes it inconvenient. And there's tooling to work around that.
    – Mathieu Guindon
    Nov 16 at 17:57






  • 2




    @R.Binter VBA doesn't do class ineritance, so you couldn't have an aluminum sub-type, but you can indeed have as many classes implementing IMaterial as you need. IMO we all missed a fundamental point though: this is data, not code. If the code is hosted in Excel, have the data live on a (hidden?) worksheet table (or a db table in Access) - then you only need one implementation of the interface, say, a Material class, and the rest of the code can be written against IMaterial, and all instances can be stored in a Dictionary with the Symbol as a key, making it trivial to retrieve.
    – Mathieu Guindon
    Nov 16 at 18:22






  • 2




    ...basically I'd challenge the premise that Aluminum.Density even needs to exist; the code should only need to know about IMaterial, and not need to care whether it's working with Aluminum or Titanium or Gold. Much code gets slashed away if the code is the same regardless of what material you're working with!
    – Mathieu Guindon
    Nov 16 at 18:27






  • 3




    @MathieuGuindon I love seeing your posts. Makes vba not seem like a child's tool <3
    – Doug Coats
    Nov 16 at 18:30


















up vote
2
down vote













You can create a Module called "ALUMINUM" and put the following inside it:



Public Const Density As Double = 169.34
Public Const Symbol As String = "AL"


Now in another module you can call into these like this:



Sub test()
Debug.Print ALUMINUM.Density
Debug.Print ALUMINUM.Symbol
End Sub





share|improve this answer























  • So, option 1 then
    – Mathieu Guindon
    Nov 16 at 17:22






  • 1




    @MathieuGuindon No, the module name is important because it allows the dot the OP wants.
    – GSerg
    Nov 16 at 17:23










  • @MathieuGuindon true... I guess I don't see how to have a ton of constants without having a ton of constants... This at least gets to the "." behavior that the OP described
    – ArcherBird
    Nov 16 at 17:23












  • That is a neat idea that I didn't think of but I think I would prefer having a lot of constants to having a lot of modules. I might have 20+ materials so that's a lot of modules also
    – R. Binter
    Nov 16 at 17:26










  • @R.Binter - I get what you mean, but I think no matter what approach you take either constants or properties, there is going to be some sort of Data-Entry-Like work to get your constants set up in a structured way.
    – ArcherBird
    Nov 16 at 17:29




















up vote
2
down vote













You could create a Class module -- let's call it Material, and define the properties a material has as public members (variables), like Density, Symbol:



Public Density As Float
Public Symbol As String


Then in a standard module create the materials:



Public Aluminium As New Material
Aluminium.Density = 169.34
Aluminium.Symbol = "AL"

Public Copper As New Material
' ... etc


Adding behaviour



The nice thing about classes is that you can define functions in it (methods) which you can also call with the dot notation on any instance. For example, if could define in the class:



Public Function AsString() 
AsString = Symbol & "(" & Density & ")"
End Function


...then with your instance Aluminium (see earlier) you can do:



MsgBox Aluminium.AsString() ' =>   "AL(169.34)"


And whenever you have a new feature/behaviour to implement that must be available for all materials, you only have to implement it in the class.



Another example. Define in the class:



Public Function CalculateWeight(Volume As Float) As Float
CalculateWeight = Volume * Density
End Function


...and you can now do:



Weight = Aluminium.CalculateWeight(50.6)


Making the properties read-only



If you want to be sure that your code does not assign a new value to the Density and Symbol properties, then you need a bit more code. In the class you would define those properties with getters and setters (using Get and Set syntax). For example, Symbol would be defined as follows:



Private privSymbol as String

Property Get Symbol() As String
Symbol = privSymbol
End Property

Property Set Symbol(value As String)
If privSymbol = "" Then privSymbol = value
End Property


The above code will only allow to set the Symbol property if it is different from the empty string. Once set to "AL" it cannot be changed any more. You might even want to raise an error if such an attempt is made.






share|improve this answer



















  • 1




    this is a good suggestion but I think code could potentially set Aluminum.Density = 2 since I don't think it's really a constant
    – R. Binter
    Nov 16 at 17:46










  • Indeed, @R.Binter, when one doesn't trust their own code to not change it, then you can make it a getter/setter property that only allows one change to the property (for initialisation) and no more. I added the suggest code for that to my answer.
    – trincot
    Nov 16 at 18:06






  • 2




    when one doesn't trust their own code - say, why do we make anything private in C#? Or final in Java? It annoys me to no end that the attitude is "bah, doesn't really matter" only when the language is VBA.
    – Mathieu Guindon
    Nov 16 at 18:34










  • Well, VBA is a long way from truly object oriented languages. For one it lacks a constructor accepting arguments. That would really be the way to go to initialise an instance, but it's not possible in VBA (unless I'm missing something), so we get into a kind of hacky way to almost get there. I'm not completely convinced that such workarounds are better than just living with the fact that VBA is not all that fantastic.
    – trincot
    Nov 16 at 21:12










  • Factories to the rescue - paired with a stateless default instance and a get-only interface, you get to do Dim aluminum As IMaterial: Set aluminum = Material.Create("AL", 169.34). Here's a whole Battleship game written in full-blown OOP, 100% VBA. Factory methods, adapter patterns, Model-View-Controller architecture... looks clean AF to me. the only thing VBA doesn't do is class inheritance.
    – Mathieu Guindon
    Nov 16 at 21:26


















up vote
0
down vote













I like a hybrid approach. This is pseudo code because I don't quite have the time to fully work the example.



Create a MaterialsDataClass - see Mathieu Guindon's knowledge about setting this up as a static class



Private ArrayOfSymbols() as String
Private ArrayOfDensity() as Double
Private ArrayOfName() as String
' ....

ArrayOfSymbols = Split("H|He|AL|O|...","|")
ArrayOfDensity = '....
ArrayOfName = '....

Property Get GetMaterialBySymbol(value as Variant) as Material
Dim Index as Long
Dim NewMaterial as Material
'Find value in the Symbol array, get the Index
New Material = SetNewMaterial(ArrayOfSymbols(Index), ArrayofName(Index), ArrayofDensity(Index))
GetMaterialBySymbol = NewMaterial
End Property

Property Get GetMaterialByName(value as string) ' etc.


Material itself is similar to other answers. I have used a Type below, but I prefer Classes over Types because they allow more functionality, and they also can be used in 'For Each' loops.



Public Type Material
Density As Double
Symbol As String
Name as String
End Type


In your usage:



Public MaterialsData as New MaterialsDataClass
Dim MyMaterial as Material
Set MyMaterial = MaterialsDataClass.GetMaterialByName("Aluminium")
Debug.print MyMaterial.Density





share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53342482%2fconstant-with-dot-operator-vba%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    5 Answers
    5






    active

    oldest

    votes








    5 Answers
    5






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    12
    down vote



    accepted










    Make VBA's equivalent to a "static class". Regular modules can have properties, and nothing says that they can't be read-only. I'd also wrap the density and symbol up in a type:



    'Materials.bas

    Public Type Material
    Density As Double
    Symbol As String
    End Type

    Public Property Get Aluminum() As Material
    Dim output As Material
    output.Density = 169.34
    output.Symbol = "AL"
    Aluminum = output
    End Property

    Public Property Get Iron() As Material
    '... etc
    End Property


    This gets pretty close to your desired usage semantics:



    Private Sub Example()
    Debug.Print Materials.Aluminum.Density
    Debug.Print Materials.Aluminum.Symbol
    End Sub


    If you're in the same project, you can even drop the explicit Materials qualifier (although I'd recommend making it explicit):



    Private Sub Example()
    Debug.Print Aluminum.Density
    Debug.Print Aluminum.Symbol
    End Sub





    share|improve this answer

















    • 1




      I like that, although then code that does Materials.Aluminum.Density = 42 from outside will compile, even though will do nothing.
      – GSerg
      Nov 16 at 17:38










    • @GSerg - Yeah, that's one issue - the returned Material really isn't a constant - there's nothing to prevent the caller from grabbing a reference to it. Ideally I'd combine this with one of the solutions from this QA to make the return value immutable, but that's a bit above and beyond.
      – Comintern
      Nov 16 at 17:47










    • No, it's safe in the sense that the caller cannot mess it up, because Material is a value type. It's just that it allows non-sensible code to compile instead of giving an error of some sort.
      – GSerg
      Nov 16 at 17:49












    • I like that! it gives easy to read notation, its value can't be modified, it lets me even pass the result like a variable with an enforced type. Example: Function CalculateWeight(Mat As Material, Volume as float) End Function Weight = CalculateWeight(Materials.Aluminum, 50.6)
      – R. Binter
      Nov 16 at 17:49








    • 1




      @GSerg - Right, but you do need to be aware that you shouldn't Dim x As Material: x = Materials.Aluminum and then start messing around with it locally.
      – Comintern
      Nov 16 at 17:51















    up vote
    12
    down vote



    accepted










    Make VBA's equivalent to a "static class". Regular modules can have properties, and nothing says that they can't be read-only. I'd also wrap the density and symbol up in a type:



    'Materials.bas

    Public Type Material
    Density As Double
    Symbol As String
    End Type

    Public Property Get Aluminum() As Material
    Dim output As Material
    output.Density = 169.34
    output.Symbol = "AL"
    Aluminum = output
    End Property

    Public Property Get Iron() As Material
    '... etc
    End Property


    This gets pretty close to your desired usage semantics:



    Private Sub Example()
    Debug.Print Materials.Aluminum.Density
    Debug.Print Materials.Aluminum.Symbol
    End Sub


    If you're in the same project, you can even drop the explicit Materials qualifier (although I'd recommend making it explicit):



    Private Sub Example()
    Debug.Print Aluminum.Density
    Debug.Print Aluminum.Symbol
    End Sub





    share|improve this answer

















    • 1




      I like that, although then code that does Materials.Aluminum.Density = 42 from outside will compile, even though will do nothing.
      – GSerg
      Nov 16 at 17:38










    • @GSerg - Yeah, that's one issue - the returned Material really isn't a constant - there's nothing to prevent the caller from grabbing a reference to it. Ideally I'd combine this with one of the solutions from this QA to make the return value immutable, but that's a bit above and beyond.
      – Comintern
      Nov 16 at 17:47










    • No, it's safe in the sense that the caller cannot mess it up, because Material is a value type. It's just that it allows non-sensible code to compile instead of giving an error of some sort.
      – GSerg
      Nov 16 at 17:49












    • I like that! it gives easy to read notation, its value can't be modified, it lets me even pass the result like a variable with an enforced type. Example: Function CalculateWeight(Mat As Material, Volume as float) End Function Weight = CalculateWeight(Materials.Aluminum, 50.6)
      – R. Binter
      Nov 16 at 17:49








    • 1




      @GSerg - Right, but you do need to be aware that you shouldn't Dim x As Material: x = Materials.Aluminum and then start messing around with it locally.
      – Comintern
      Nov 16 at 17:51













    up vote
    12
    down vote



    accepted







    up vote
    12
    down vote



    accepted






    Make VBA's equivalent to a "static class". Regular modules can have properties, and nothing says that they can't be read-only. I'd also wrap the density and symbol up in a type:



    'Materials.bas

    Public Type Material
    Density As Double
    Symbol As String
    End Type

    Public Property Get Aluminum() As Material
    Dim output As Material
    output.Density = 169.34
    output.Symbol = "AL"
    Aluminum = output
    End Property

    Public Property Get Iron() As Material
    '... etc
    End Property


    This gets pretty close to your desired usage semantics:



    Private Sub Example()
    Debug.Print Materials.Aluminum.Density
    Debug.Print Materials.Aluminum.Symbol
    End Sub


    If you're in the same project, you can even drop the explicit Materials qualifier (although I'd recommend making it explicit):



    Private Sub Example()
    Debug.Print Aluminum.Density
    Debug.Print Aluminum.Symbol
    End Sub





    share|improve this answer












    Make VBA's equivalent to a "static class". Regular modules can have properties, and nothing says that they can't be read-only. I'd also wrap the density and symbol up in a type:



    'Materials.bas

    Public Type Material
    Density As Double
    Symbol As String
    End Type

    Public Property Get Aluminum() As Material
    Dim output As Material
    output.Density = 169.34
    output.Symbol = "AL"
    Aluminum = output
    End Property

    Public Property Get Iron() As Material
    '... etc
    End Property


    This gets pretty close to your desired usage semantics:



    Private Sub Example()
    Debug.Print Materials.Aluminum.Density
    Debug.Print Materials.Aluminum.Symbol
    End Sub


    If you're in the same project, you can even drop the explicit Materials qualifier (although I'd recommend making it explicit):



    Private Sub Example()
    Debug.Print Aluminum.Density
    Debug.Print Aluminum.Symbol
    End Sub






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 16 at 17:30









    Comintern

    17.7k42354




    17.7k42354








    • 1




      I like that, although then code that does Materials.Aluminum.Density = 42 from outside will compile, even though will do nothing.
      – GSerg
      Nov 16 at 17:38










    • @GSerg - Yeah, that's one issue - the returned Material really isn't a constant - there's nothing to prevent the caller from grabbing a reference to it. Ideally I'd combine this with one of the solutions from this QA to make the return value immutable, but that's a bit above and beyond.
      – Comintern
      Nov 16 at 17:47










    • No, it's safe in the sense that the caller cannot mess it up, because Material is a value type. It's just that it allows non-sensible code to compile instead of giving an error of some sort.
      – GSerg
      Nov 16 at 17:49












    • I like that! it gives easy to read notation, its value can't be modified, it lets me even pass the result like a variable with an enforced type. Example: Function CalculateWeight(Mat As Material, Volume as float) End Function Weight = CalculateWeight(Materials.Aluminum, 50.6)
      – R. Binter
      Nov 16 at 17:49








    • 1




      @GSerg - Right, but you do need to be aware that you shouldn't Dim x As Material: x = Materials.Aluminum and then start messing around with it locally.
      – Comintern
      Nov 16 at 17:51














    • 1




      I like that, although then code that does Materials.Aluminum.Density = 42 from outside will compile, even though will do nothing.
      – GSerg
      Nov 16 at 17:38










    • @GSerg - Yeah, that's one issue - the returned Material really isn't a constant - there's nothing to prevent the caller from grabbing a reference to it. Ideally I'd combine this with one of the solutions from this QA to make the return value immutable, but that's a bit above and beyond.
      – Comintern
      Nov 16 at 17:47










    • No, it's safe in the sense that the caller cannot mess it up, because Material is a value type. It's just that it allows non-sensible code to compile instead of giving an error of some sort.
      – GSerg
      Nov 16 at 17:49












    • I like that! it gives easy to read notation, its value can't be modified, it lets me even pass the result like a variable with an enforced type. Example: Function CalculateWeight(Mat As Material, Volume as float) End Function Weight = CalculateWeight(Materials.Aluminum, 50.6)
      – R. Binter
      Nov 16 at 17:49








    • 1




      @GSerg - Right, but you do need to be aware that you shouldn't Dim x As Material: x = Materials.Aluminum and then start messing around with it locally.
      – Comintern
      Nov 16 at 17:51








    1




    1




    I like that, although then code that does Materials.Aluminum.Density = 42 from outside will compile, even though will do nothing.
    – GSerg
    Nov 16 at 17:38




    I like that, although then code that does Materials.Aluminum.Density = 42 from outside will compile, even though will do nothing.
    – GSerg
    Nov 16 at 17:38












    @GSerg - Yeah, that's one issue - the returned Material really isn't a constant - there's nothing to prevent the caller from grabbing a reference to it. Ideally I'd combine this with one of the solutions from this QA to make the return value immutable, but that's a bit above and beyond.
    – Comintern
    Nov 16 at 17:47




    @GSerg - Yeah, that's one issue - the returned Material really isn't a constant - there's nothing to prevent the caller from grabbing a reference to it. Ideally I'd combine this with one of the solutions from this QA to make the return value immutable, but that's a bit above and beyond.
    – Comintern
    Nov 16 at 17:47












    No, it's safe in the sense that the caller cannot mess it up, because Material is a value type. It's just that it allows non-sensible code to compile instead of giving an error of some sort.
    – GSerg
    Nov 16 at 17:49






    No, it's safe in the sense that the caller cannot mess it up, because Material is a value type. It's just that it allows non-sensible code to compile instead of giving an error of some sort.
    – GSerg
    Nov 16 at 17:49














    I like that! it gives easy to read notation, its value can't be modified, it lets me even pass the result like a variable with an enforced type. Example: Function CalculateWeight(Mat As Material, Volume as float) End Function Weight = CalculateWeight(Materials.Aluminum, 50.6)
    – R. Binter
    Nov 16 at 17:49






    I like that! it gives easy to read notation, its value can't be modified, it lets me even pass the result like a variable with an enforced type. Example: Function CalculateWeight(Mat As Material, Volume as float) End Function Weight = CalculateWeight(Materials.Aluminum, 50.6)
    – R. Binter
    Nov 16 at 17:49






    1




    1




    @GSerg - Right, but you do need to be aware that you shouldn't Dim x As Material: x = Materials.Aluminum and then start messing around with it locally.
    – Comintern
    Nov 16 at 17:51




    @GSerg - Right, but you do need to be aware that you shouldn't Dim x As Material: x = Materials.Aluminum and then start messing around with it locally.
    – Comintern
    Nov 16 at 17:51












    up vote
    5
    down vote













    IMO @Comintern hit the nail on the head; this answer is just another possible alternative.





    Make an interface for it. Add a class module, call it IMaterial; that interface will formalize the get-only properties a Material needs:



    Option Explicit
    Public Property Get Symbol() As String
    End Property

    Public Property Get Density() As Single
    End Property


    Now bring up Notepad and paste this class header:



    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "StaticClass1"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit


    Save it as StaticClass1.cls and keep it in your "frequently needed VBA code files" folder (make one if you don't have one!).



    Now add a prototype implementation to the text file:



    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "Material"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit
    Implements IMaterial

    Private Const mSymbol As String = ""
    Private Const mDensity As Single = 0

    Private Property Get IMaterial_Symbol() As String
    IMaterial_Symbol = Symbol
    End Property

    Private Property Get IMaterial_Density() As Single
    IMaterial_Density = Density
    End Property

    Public Property Get Symbol() As String
    Symbol = mSymbol
    End Property

    Public Property Get Density() As Single
    Density = mDensity
    End Property


    Save that text file as Material.cls.



    Now import this Material class into your project; rename it to AluminiumMaterial, and fill in the blanks:



    Private Const mSymbol As String = "AL"
    Private Const mDensity As Single = 169.34


    Import the Material class again, rename it to AnotherMaterial, fill in the blanks:



    Private Const mSymbol As String = "XYZ"
    Private Const mDensity As Single = 123.45


    Rinse & repeat for every material: you only need to supply each value once per material.



    If you're using Rubberduck, add a folder annotation to the template file:



    '@Folder("Materials")


    And then the Code Explorer will cleanly regroup all the IMaterial classes under a Materials folder.



    Having "many modules" is only a problem in VBA because the VBE's Project Explorer makes it rather inconvenient (by stuffing every single class under a single "classes" folder). Rubberduck's Code Explorer won't make VBA have namespaces, but lets you organize your VBA project in a structured way regardless.



    Usage-wise, you can now have polymorphic code written against the IMaterial interface:



    Public Sub DoSomething(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
    End Sub


    Or you can access the get-only properties from the exposed default instance (that you get from the modules' VB_PredeclaredId = True attribute):



    Public Sub DoSomething()
    Debug.Print AluminumMaterial.Symbol, AluminumMaterial.Density
    End Sub


    And you can pass the default instances around into any method that needs to work with an IMaterial:



    Public Sub DoSomething()
    PrintToDebugPane AluminumMaterial
    End Sub

    Private Sub PrintToDebugPane(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
    End Sub


    Upsides, you get compile-time validation for everything; the types are impossible to misuse.



    Downsides, you need many modules (classes), and if the interface needs to change that makes a lot of classes to update to keep the code compilable.






    share|improve this answer





















    • That way we end up with a bunch of classes, and the OP has already disliked a bunch of modules.
      – GSerg
      Nov 16 at 17:52








    • 2




      @GSerg I know, and I believe this answer already states this clearly. Yet it's how you get 100% type safety and compiler-assisted consistency, without exposing a mutable UDT (which have frustrating limitations in VBA). It's a solution worth considering regardless of what the OP thinks of having many modules. Having many classes isn't a problem in any OOP language; my take is that it's only a problem in VBA because the IDE makes it inconvenient. And there's tooling to work around that.
      – Mathieu Guindon
      Nov 16 at 17:57






    • 2




      @R.Binter VBA doesn't do class ineritance, so you couldn't have an aluminum sub-type, but you can indeed have as many classes implementing IMaterial as you need. IMO we all missed a fundamental point though: this is data, not code. If the code is hosted in Excel, have the data live on a (hidden?) worksheet table (or a db table in Access) - then you only need one implementation of the interface, say, a Material class, and the rest of the code can be written against IMaterial, and all instances can be stored in a Dictionary with the Symbol as a key, making it trivial to retrieve.
      – Mathieu Guindon
      Nov 16 at 18:22






    • 2




      ...basically I'd challenge the premise that Aluminum.Density even needs to exist; the code should only need to know about IMaterial, and not need to care whether it's working with Aluminum or Titanium or Gold. Much code gets slashed away if the code is the same regardless of what material you're working with!
      – Mathieu Guindon
      Nov 16 at 18:27






    • 3




      @MathieuGuindon I love seeing your posts. Makes vba not seem like a child's tool <3
      – Doug Coats
      Nov 16 at 18:30















    up vote
    5
    down vote













    IMO @Comintern hit the nail on the head; this answer is just another possible alternative.





    Make an interface for it. Add a class module, call it IMaterial; that interface will formalize the get-only properties a Material needs:



    Option Explicit
    Public Property Get Symbol() As String
    End Property

    Public Property Get Density() As Single
    End Property


    Now bring up Notepad and paste this class header:



    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "StaticClass1"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit


    Save it as StaticClass1.cls and keep it in your "frequently needed VBA code files" folder (make one if you don't have one!).



    Now add a prototype implementation to the text file:



    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "Material"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit
    Implements IMaterial

    Private Const mSymbol As String = ""
    Private Const mDensity As Single = 0

    Private Property Get IMaterial_Symbol() As String
    IMaterial_Symbol = Symbol
    End Property

    Private Property Get IMaterial_Density() As Single
    IMaterial_Density = Density
    End Property

    Public Property Get Symbol() As String
    Symbol = mSymbol
    End Property

    Public Property Get Density() As Single
    Density = mDensity
    End Property


    Save that text file as Material.cls.



    Now import this Material class into your project; rename it to AluminiumMaterial, and fill in the blanks:



    Private Const mSymbol As String = "AL"
    Private Const mDensity As Single = 169.34


    Import the Material class again, rename it to AnotherMaterial, fill in the blanks:



    Private Const mSymbol As String = "XYZ"
    Private Const mDensity As Single = 123.45


    Rinse & repeat for every material: you only need to supply each value once per material.



    If you're using Rubberduck, add a folder annotation to the template file:



    '@Folder("Materials")


    And then the Code Explorer will cleanly regroup all the IMaterial classes under a Materials folder.



    Having "many modules" is only a problem in VBA because the VBE's Project Explorer makes it rather inconvenient (by stuffing every single class under a single "classes" folder). Rubberduck's Code Explorer won't make VBA have namespaces, but lets you organize your VBA project in a structured way regardless.



    Usage-wise, you can now have polymorphic code written against the IMaterial interface:



    Public Sub DoSomething(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
    End Sub


    Or you can access the get-only properties from the exposed default instance (that you get from the modules' VB_PredeclaredId = True attribute):



    Public Sub DoSomething()
    Debug.Print AluminumMaterial.Symbol, AluminumMaterial.Density
    End Sub


    And you can pass the default instances around into any method that needs to work with an IMaterial:



    Public Sub DoSomething()
    PrintToDebugPane AluminumMaterial
    End Sub

    Private Sub PrintToDebugPane(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
    End Sub


    Upsides, you get compile-time validation for everything; the types are impossible to misuse.



    Downsides, you need many modules (classes), and if the interface needs to change that makes a lot of classes to update to keep the code compilable.






    share|improve this answer





















    • That way we end up with a bunch of classes, and the OP has already disliked a bunch of modules.
      – GSerg
      Nov 16 at 17:52








    • 2




      @GSerg I know, and I believe this answer already states this clearly. Yet it's how you get 100% type safety and compiler-assisted consistency, without exposing a mutable UDT (which have frustrating limitations in VBA). It's a solution worth considering regardless of what the OP thinks of having many modules. Having many classes isn't a problem in any OOP language; my take is that it's only a problem in VBA because the IDE makes it inconvenient. And there's tooling to work around that.
      – Mathieu Guindon
      Nov 16 at 17:57






    • 2




      @R.Binter VBA doesn't do class ineritance, so you couldn't have an aluminum sub-type, but you can indeed have as many classes implementing IMaterial as you need. IMO we all missed a fundamental point though: this is data, not code. If the code is hosted in Excel, have the data live on a (hidden?) worksheet table (or a db table in Access) - then you only need one implementation of the interface, say, a Material class, and the rest of the code can be written against IMaterial, and all instances can be stored in a Dictionary with the Symbol as a key, making it trivial to retrieve.
      – Mathieu Guindon
      Nov 16 at 18:22






    • 2




      ...basically I'd challenge the premise that Aluminum.Density even needs to exist; the code should only need to know about IMaterial, and not need to care whether it's working with Aluminum or Titanium or Gold. Much code gets slashed away if the code is the same regardless of what material you're working with!
      – Mathieu Guindon
      Nov 16 at 18:27






    • 3




      @MathieuGuindon I love seeing your posts. Makes vba not seem like a child's tool <3
      – Doug Coats
      Nov 16 at 18:30













    up vote
    5
    down vote










    up vote
    5
    down vote









    IMO @Comintern hit the nail on the head; this answer is just another possible alternative.





    Make an interface for it. Add a class module, call it IMaterial; that interface will formalize the get-only properties a Material needs:



    Option Explicit
    Public Property Get Symbol() As String
    End Property

    Public Property Get Density() As Single
    End Property


    Now bring up Notepad and paste this class header:



    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "StaticClass1"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit


    Save it as StaticClass1.cls and keep it in your "frequently needed VBA code files" folder (make one if you don't have one!).



    Now add a prototype implementation to the text file:



    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "Material"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit
    Implements IMaterial

    Private Const mSymbol As String = ""
    Private Const mDensity As Single = 0

    Private Property Get IMaterial_Symbol() As String
    IMaterial_Symbol = Symbol
    End Property

    Private Property Get IMaterial_Density() As Single
    IMaterial_Density = Density
    End Property

    Public Property Get Symbol() As String
    Symbol = mSymbol
    End Property

    Public Property Get Density() As Single
    Density = mDensity
    End Property


    Save that text file as Material.cls.



    Now import this Material class into your project; rename it to AluminiumMaterial, and fill in the blanks:



    Private Const mSymbol As String = "AL"
    Private Const mDensity As Single = 169.34


    Import the Material class again, rename it to AnotherMaterial, fill in the blanks:



    Private Const mSymbol As String = "XYZ"
    Private Const mDensity As Single = 123.45


    Rinse & repeat for every material: you only need to supply each value once per material.



    If you're using Rubberduck, add a folder annotation to the template file:



    '@Folder("Materials")


    And then the Code Explorer will cleanly regroup all the IMaterial classes under a Materials folder.



    Having "many modules" is only a problem in VBA because the VBE's Project Explorer makes it rather inconvenient (by stuffing every single class under a single "classes" folder). Rubberduck's Code Explorer won't make VBA have namespaces, but lets you organize your VBA project in a structured way regardless.



    Usage-wise, you can now have polymorphic code written against the IMaterial interface:



    Public Sub DoSomething(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
    End Sub


    Or you can access the get-only properties from the exposed default instance (that you get from the modules' VB_PredeclaredId = True attribute):



    Public Sub DoSomething()
    Debug.Print AluminumMaterial.Symbol, AluminumMaterial.Density
    End Sub


    And you can pass the default instances around into any method that needs to work with an IMaterial:



    Public Sub DoSomething()
    PrintToDebugPane AluminumMaterial
    End Sub

    Private Sub PrintToDebugPane(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
    End Sub


    Upsides, you get compile-time validation for everything; the types are impossible to misuse.



    Downsides, you need many modules (classes), and if the interface needs to change that makes a lot of classes to update to keep the code compilable.






    share|improve this answer












    IMO @Comintern hit the nail on the head; this answer is just another possible alternative.





    Make an interface for it. Add a class module, call it IMaterial; that interface will formalize the get-only properties a Material needs:



    Option Explicit
    Public Property Get Symbol() As String
    End Property

    Public Property Get Density() As Single
    End Property


    Now bring up Notepad and paste this class header:



    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "StaticClass1"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit


    Save it as StaticClass1.cls and keep it in your "frequently needed VBA code files" folder (make one if you don't have one!).



    Now add a prototype implementation to the text file:



    VERSION 1.0 CLASS
    BEGIN
    MultiUse = -1 'True
    END
    Attribute VB_Name = "Material"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    Option Explicit
    Implements IMaterial

    Private Const mSymbol As String = ""
    Private Const mDensity As Single = 0

    Private Property Get IMaterial_Symbol() As String
    IMaterial_Symbol = Symbol
    End Property

    Private Property Get IMaterial_Density() As Single
    IMaterial_Density = Density
    End Property

    Public Property Get Symbol() As String
    Symbol = mSymbol
    End Property

    Public Property Get Density() As Single
    Density = mDensity
    End Property


    Save that text file as Material.cls.



    Now import this Material class into your project; rename it to AluminiumMaterial, and fill in the blanks:



    Private Const mSymbol As String = "AL"
    Private Const mDensity As Single = 169.34


    Import the Material class again, rename it to AnotherMaterial, fill in the blanks:



    Private Const mSymbol As String = "XYZ"
    Private Const mDensity As Single = 123.45


    Rinse & repeat for every material: you only need to supply each value once per material.



    If you're using Rubberduck, add a folder annotation to the template file:



    '@Folder("Materials")


    And then the Code Explorer will cleanly regroup all the IMaterial classes under a Materials folder.



    Having "many modules" is only a problem in VBA because the VBE's Project Explorer makes it rather inconvenient (by stuffing every single class under a single "classes" folder). Rubberduck's Code Explorer won't make VBA have namespaces, but lets you organize your VBA project in a structured way regardless.



    Usage-wise, you can now have polymorphic code written against the IMaterial interface:



    Public Sub DoSomething(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
    End Sub


    Or you can access the get-only properties from the exposed default instance (that you get from the modules' VB_PredeclaredId = True attribute):



    Public Sub DoSomething()
    Debug.Print AluminumMaterial.Symbol, AluminumMaterial.Density
    End Sub


    And you can pass the default instances around into any method that needs to work with an IMaterial:



    Public Sub DoSomething()
    PrintToDebugPane AluminumMaterial
    End Sub

    Private Sub PrintToDebugPane(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
    End Sub


    Upsides, you get compile-time validation for everything; the types are impossible to misuse.



    Downsides, you need many modules (classes), and if the interface needs to change that makes a lot of classes to update to keep the code compilable.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 16 at 17:48









    Mathieu Guindon

    39.6k761137




    39.6k761137












    • That way we end up with a bunch of classes, and the OP has already disliked a bunch of modules.
      – GSerg
      Nov 16 at 17:52








    • 2




      @GSerg I know, and I believe this answer already states this clearly. Yet it's how you get 100% type safety and compiler-assisted consistency, without exposing a mutable UDT (which have frustrating limitations in VBA). It's a solution worth considering regardless of what the OP thinks of having many modules. Having many classes isn't a problem in any OOP language; my take is that it's only a problem in VBA because the IDE makes it inconvenient. And there's tooling to work around that.
      – Mathieu Guindon
      Nov 16 at 17:57






    • 2




      @R.Binter VBA doesn't do class ineritance, so you couldn't have an aluminum sub-type, but you can indeed have as many classes implementing IMaterial as you need. IMO we all missed a fundamental point though: this is data, not code. If the code is hosted in Excel, have the data live on a (hidden?) worksheet table (or a db table in Access) - then you only need one implementation of the interface, say, a Material class, and the rest of the code can be written against IMaterial, and all instances can be stored in a Dictionary with the Symbol as a key, making it trivial to retrieve.
      – Mathieu Guindon
      Nov 16 at 18:22






    • 2




      ...basically I'd challenge the premise that Aluminum.Density even needs to exist; the code should only need to know about IMaterial, and not need to care whether it's working with Aluminum or Titanium or Gold. Much code gets slashed away if the code is the same regardless of what material you're working with!
      – Mathieu Guindon
      Nov 16 at 18:27






    • 3




      @MathieuGuindon I love seeing your posts. Makes vba not seem like a child's tool <3
      – Doug Coats
      Nov 16 at 18:30


















    • That way we end up with a bunch of classes, and the OP has already disliked a bunch of modules.
      – GSerg
      Nov 16 at 17:52








    • 2




      @GSerg I know, and I believe this answer already states this clearly. Yet it's how you get 100% type safety and compiler-assisted consistency, without exposing a mutable UDT (which have frustrating limitations in VBA). It's a solution worth considering regardless of what the OP thinks of having many modules. Having many classes isn't a problem in any OOP language; my take is that it's only a problem in VBA because the IDE makes it inconvenient. And there's tooling to work around that.
      – Mathieu Guindon
      Nov 16 at 17:57






    • 2




      @R.Binter VBA doesn't do class ineritance, so you couldn't have an aluminum sub-type, but you can indeed have as many classes implementing IMaterial as you need. IMO we all missed a fundamental point though: this is data, not code. If the code is hosted in Excel, have the data live on a (hidden?) worksheet table (or a db table in Access) - then you only need one implementation of the interface, say, a Material class, and the rest of the code can be written against IMaterial, and all instances can be stored in a Dictionary with the Symbol as a key, making it trivial to retrieve.
      – Mathieu Guindon
      Nov 16 at 18:22






    • 2




      ...basically I'd challenge the premise that Aluminum.Density even needs to exist; the code should only need to know about IMaterial, and not need to care whether it's working with Aluminum or Titanium or Gold. Much code gets slashed away if the code is the same regardless of what material you're working with!
      – Mathieu Guindon
      Nov 16 at 18:27






    • 3




      @MathieuGuindon I love seeing your posts. Makes vba not seem like a child's tool <3
      – Doug Coats
      Nov 16 at 18:30
















    That way we end up with a bunch of classes, and the OP has already disliked a bunch of modules.
    – GSerg
    Nov 16 at 17:52






    That way we end up with a bunch of classes, and the OP has already disliked a bunch of modules.
    – GSerg
    Nov 16 at 17:52






    2




    2




    @GSerg I know, and I believe this answer already states this clearly. Yet it's how you get 100% type safety and compiler-assisted consistency, without exposing a mutable UDT (which have frustrating limitations in VBA). It's a solution worth considering regardless of what the OP thinks of having many modules. Having many classes isn't a problem in any OOP language; my take is that it's only a problem in VBA because the IDE makes it inconvenient. And there's tooling to work around that.
    – Mathieu Guindon
    Nov 16 at 17:57




    @GSerg I know, and I believe this answer already states this clearly. Yet it's how you get 100% type safety and compiler-assisted consistency, without exposing a mutable UDT (which have frustrating limitations in VBA). It's a solution worth considering regardless of what the OP thinks of having many modules. Having many classes isn't a problem in any OOP language; my take is that it's only a problem in VBA because the IDE makes it inconvenient. And there's tooling to work around that.
    – Mathieu Guindon
    Nov 16 at 17:57




    2




    2




    @R.Binter VBA doesn't do class ineritance, so you couldn't have an aluminum sub-type, but you can indeed have as many classes implementing IMaterial as you need. IMO we all missed a fundamental point though: this is data, not code. If the code is hosted in Excel, have the data live on a (hidden?) worksheet table (or a db table in Access) - then you only need one implementation of the interface, say, a Material class, and the rest of the code can be written against IMaterial, and all instances can be stored in a Dictionary with the Symbol as a key, making it trivial to retrieve.
    – Mathieu Guindon
    Nov 16 at 18:22




    @R.Binter VBA doesn't do class ineritance, so you couldn't have an aluminum sub-type, but you can indeed have as many classes implementing IMaterial as you need. IMO we all missed a fundamental point though: this is data, not code. If the code is hosted in Excel, have the data live on a (hidden?) worksheet table (or a db table in Access) - then you only need one implementation of the interface, say, a Material class, and the rest of the code can be written against IMaterial, and all instances can be stored in a Dictionary with the Symbol as a key, making it trivial to retrieve.
    – Mathieu Guindon
    Nov 16 at 18:22




    2




    2




    ...basically I'd challenge the premise that Aluminum.Density even needs to exist; the code should only need to know about IMaterial, and not need to care whether it's working with Aluminum or Titanium or Gold. Much code gets slashed away if the code is the same regardless of what material you're working with!
    – Mathieu Guindon
    Nov 16 at 18:27




    ...basically I'd challenge the premise that Aluminum.Density even needs to exist; the code should only need to know about IMaterial, and not need to care whether it's working with Aluminum or Titanium or Gold. Much code gets slashed away if the code is the same regardless of what material you're working with!
    – Mathieu Guindon
    Nov 16 at 18:27




    3




    3




    @MathieuGuindon I love seeing your posts. Makes vba not seem like a child's tool <3
    – Doug Coats
    Nov 16 at 18:30




    @MathieuGuindon I love seeing your posts. Makes vba not seem like a child's tool <3
    – Doug Coats
    Nov 16 at 18:30










    up vote
    2
    down vote













    You can create a Module called "ALUMINUM" and put the following inside it:



    Public Const Density As Double = 169.34
    Public Const Symbol As String = "AL"


    Now in another module you can call into these like this:



    Sub test()
    Debug.Print ALUMINUM.Density
    Debug.Print ALUMINUM.Symbol
    End Sub





    share|improve this answer























    • So, option 1 then
      – Mathieu Guindon
      Nov 16 at 17:22






    • 1




      @MathieuGuindon No, the module name is important because it allows the dot the OP wants.
      – GSerg
      Nov 16 at 17:23










    • @MathieuGuindon true... I guess I don't see how to have a ton of constants without having a ton of constants... This at least gets to the "." behavior that the OP described
      – ArcherBird
      Nov 16 at 17:23












    • That is a neat idea that I didn't think of but I think I would prefer having a lot of constants to having a lot of modules. I might have 20+ materials so that's a lot of modules also
      – R. Binter
      Nov 16 at 17:26










    • @R.Binter - I get what you mean, but I think no matter what approach you take either constants or properties, there is going to be some sort of Data-Entry-Like work to get your constants set up in a structured way.
      – ArcherBird
      Nov 16 at 17:29

















    up vote
    2
    down vote













    You can create a Module called "ALUMINUM" and put the following inside it:



    Public Const Density As Double = 169.34
    Public Const Symbol As String = "AL"


    Now in another module you can call into these like this:



    Sub test()
    Debug.Print ALUMINUM.Density
    Debug.Print ALUMINUM.Symbol
    End Sub





    share|improve this answer























    • So, option 1 then
      – Mathieu Guindon
      Nov 16 at 17:22






    • 1




      @MathieuGuindon No, the module name is important because it allows the dot the OP wants.
      – GSerg
      Nov 16 at 17:23










    • @MathieuGuindon true... I guess I don't see how to have a ton of constants without having a ton of constants... This at least gets to the "." behavior that the OP described
      – ArcherBird
      Nov 16 at 17:23












    • That is a neat idea that I didn't think of but I think I would prefer having a lot of constants to having a lot of modules. I might have 20+ materials so that's a lot of modules also
      – R. Binter
      Nov 16 at 17:26










    • @R.Binter - I get what you mean, but I think no matter what approach you take either constants or properties, there is going to be some sort of Data-Entry-Like work to get your constants set up in a structured way.
      – ArcherBird
      Nov 16 at 17:29















    up vote
    2
    down vote










    up vote
    2
    down vote









    You can create a Module called "ALUMINUM" and put the following inside it:



    Public Const Density As Double = 169.34
    Public Const Symbol As String = "AL"


    Now in another module you can call into these like this:



    Sub test()
    Debug.Print ALUMINUM.Density
    Debug.Print ALUMINUM.Symbol
    End Sub





    share|improve this answer














    You can create a Module called "ALUMINUM" and put the following inside it:



    Public Const Density As Double = 169.34
    Public Const Symbol As String = "AL"


    Now in another module you can call into these like this:



    Sub test()
    Debug.Print ALUMINUM.Density
    Debug.Print ALUMINUM.Symbol
    End Sub






    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 16 at 17:25

























    answered Nov 16 at 17:21









    ArcherBird

    705216




    705216












    • So, option 1 then
      – Mathieu Guindon
      Nov 16 at 17:22






    • 1




      @MathieuGuindon No, the module name is important because it allows the dot the OP wants.
      – GSerg
      Nov 16 at 17:23










    • @MathieuGuindon true... I guess I don't see how to have a ton of constants without having a ton of constants... This at least gets to the "." behavior that the OP described
      – ArcherBird
      Nov 16 at 17:23












    • That is a neat idea that I didn't think of but I think I would prefer having a lot of constants to having a lot of modules. I might have 20+ materials so that's a lot of modules also
      – R. Binter
      Nov 16 at 17:26










    • @R.Binter - I get what you mean, but I think no matter what approach you take either constants or properties, there is going to be some sort of Data-Entry-Like work to get your constants set up in a structured way.
      – ArcherBird
      Nov 16 at 17:29




















    • So, option 1 then
      – Mathieu Guindon
      Nov 16 at 17:22






    • 1




      @MathieuGuindon No, the module name is important because it allows the dot the OP wants.
      – GSerg
      Nov 16 at 17:23










    • @MathieuGuindon true... I guess I don't see how to have a ton of constants without having a ton of constants... This at least gets to the "." behavior that the OP described
      – ArcherBird
      Nov 16 at 17:23












    • That is a neat idea that I didn't think of but I think I would prefer having a lot of constants to having a lot of modules. I might have 20+ materials so that's a lot of modules also
      – R. Binter
      Nov 16 at 17:26










    • @R.Binter - I get what you mean, but I think no matter what approach you take either constants or properties, there is going to be some sort of Data-Entry-Like work to get your constants set up in a structured way.
      – ArcherBird
      Nov 16 at 17:29


















    So, option 1 then
    – Mathieu Guindon
    Nov 16 at 17:22




    So, option 1 then
    – Mathieu Guindon
    Nov 16 at 17:22




    1




    1




    @MathieuGuindon No, the module name is important because it allows the dot the OP wants.
    – GSerg
    Nov 16 at 17:23




    @MathieuGuindon No, the module name is important because it allows the dot the OP wants.
    – GSerg
    Nov 16 at 17:23












    @MathieuGuindon true... I guess I don't see how to have a ton of constants without having a ton of constants... This at least gets to the "." behavior that the OP described
    – ArcherBird
    Nov 16 at 17:23






    @MathieuGuindon true... I guess I don't see how to have a ton of constants without having a ton of constants... This at least gets to the "." behavior that the OP described
    – ArcherBird
    Nov 16 at 17:23














    That is a neat idea that I didn't think of but I think I would prefer having a lot of constants to having a lot of modules. I might have 20+ materials so that's a lot of modules also
    – R. Binter
    Nov 16 at 17:26




    That is a neat idea that I didn't think of but I think I would prefer having a lot of constants to having a lot of modules. I might have 20+ materials so that's a lot of modules also
    – R. Binter
    Nov 16 at 17:26












    @R.Binter - I get what you mean, but I think no matter what approach you take either constants or properties, there is going to be some sort of Data-Entry-Like work to get your constants set up in a structured way.
    – ArcherBird
    Nov 16 at 17:29






    @R.Binter - I get what you mean, but I think no matter what approach you take either constants or properties, there is going to be some sort of Data-Entry-Like work to get your constants set up in a structured way.
    – ArcherBird
    Nov 16 at 17:29












    up vote
    2
    down vote













    You could create a Class module -- let's call it Material, and define the properties a material has as public members (variables), like Density, Symbol:



    Public Density As Float
    Public Symbol As String


    Then in a standard module create the materials:



    Public Aluminium As New Material
    Aluminium.Density = 169.34
    Aluminium.Symbol = "AL"

    Public Copper As New Material
    ' ... etc


    Adding behaviour



    The nice thing about classes is that you can define functions in it (methods) which you can also call with the dot notation on any instance. For example, if could define in the class:



    Public Function AsString() 
    AsString = Symbol & "(" & Density & ")"
    End Function


    ...then with your instance Aluminium (see earlier) you can do:



    MsgBox Aluminium.AsString() ' =>   "AL(169.34)"


    And whenever you have a new feature/behaviour to implement that must be available for all materials, you only have to implement it in the class.



    Another example. Define in the class:



    Public Function CalculateWeight(Volume As Float) As Float
    CalculateWeight = Volume * Density
    End Function


    ...and you can now do:



    Weight = Aluminium.CalculateWeight(50.6)


    Making the properties read-only



    If you want to be sure that your code does not assign a new value to the Density and Symbol properties, then you need a bit more code. In the class you would define those properties with getters and setters (using Get and Set syntax). For example, Symbol would be defined as follows:



    Private privSymbol as String

    Property Get Symbol() As String
    Symbol = privSymbol
    End Property

    Property Set Symbol(value As String)
    If privSymbol = "" Then privSymbol = value
    End Property


    The above code will only allow to set the Symbol property if it is different from the empty string. Once set to "AL" it cannot be changed any more. You might even want to raise an error if such an attempt is made.






    share|improve this answer



















    • 1




      this is a good suggestion but I think code could potentially set Aluminum.Density = 2 since I don't think it's really a constant
      – R. Binter
      Nov 16 at 17:46










    • Indeed, @R.Binter, when one doesn't trust their own code to not change it, then you can make it a getter/setter property that only allows one change to the property (for initialisation) and no more. I added the suggest code for that to my answer.
      – trincot
      Nov 16 at 18:06






    • 2




      when one doesn't trust their own code - say, why do we make anything private in C#? Or final in Java? It annoys me to no end that the attitude is "bah, doesn't really matter" only when the language is VBA.
      – Mathieu Guindon
      Nov 16 at 18:34










    • Well, VBA is a long way from truly object oriented languages. For one it lacks a constructor accepting arguments. That would really be the way to go to initialise an instance, but it's not possible in VBA (unless I'm missing something), so we get into a kind of hacky way to almost get there. I'm not completely convinced that such workarounds are better than just living with the fact that VBA is not all that fantastic.
      – trincot
      Nov 16 at 21:12










    • Factories to the rescue - paired with a stateless default instance and a get-only interface, you get to do Dim aluminum As IMaterial: Set aluminum = Material.Create("AL", 169.34). Here's a whole Battleship game written in full-blown OOP, 100% VBA. Factory methods, adapter patterns, Model-View-Controller architecture... looks clean AF to me. the only thing VBA doesn't do is class inheritance.
      – Mathieu Guindon
      Nov 16 at 21:26















    up vote
    2
    down vote













    You could create a Class module -- let's call it Material, and define the properties a material has as public members (variables), like Density, Symbol:



    Public Density As Float
    Public Symbol As String


    Then in a standard module create the materials:



    Public Aluminium As New Material
    Aluminium.Density = 169.34
    Aluminium.Symbol = "AL"

    Public Copper As New Material
    ' ... etc


    Adding behaviour



    The nice thing about classes is that you can define functions in it (methods) which you can also call with the dot notation on any instance. For example, if could define in the class:



    Public Function AsString() 
    AsString = Symbol & "(" & Density & ")"
    End Function


    ...then with your instance Aluminium (see earlier) you can do:



    MsgBox Aluminium.AsString() ' =>   "AL(169.34)"


    And whenever you have a new feature/behaviour to implement that must be available for all materials, you only have to implement it in the class.



    Another example. Define in the class:



    Public Function CalculateWeight(Volume As Float) As Float
    CalculateWeight = Volume * Density
    End Function


    ...and you can now do:



    Weight = Aluminium.CalculateWeight(50.6)


    Making the properties read-only



    If you want to be sure that your code does not assign a new value to the Density and Symbol properties, then you need a bit more code. In the class you would define those properties with getters and setters (using Get and Set syntax). For example, Symbol would be defined as follows:



    Private privSymbol as String

    Property Get Symbol() As String
    Symbol = privSymbol
    End Property

    Property Set Symbol(value As String)
    If privSymbol = "" Then privSymbol = value
    End Property


    The above code will only allow to set the Symbol property if it is different from the empty string. Once set to "AL" it cannot be changed any more. You might even want to raise an error if such an attempt is made.






    share|improve this answer



















    • 1




      this is a good suggestion but I think code could potentially set Aluminum.Density = 2 since I don't think it's really a constant
      – R. Binter
      Nov 16 at 17:46










    • Indeed, @R.Binter, when one doesn't trust their own code to not change it, then you can make it a getter/setter property that only allows one change to the property (for initialisation) and no more. I added the suggest code for that to my answer.
      – trincot
      Nov 16 at 18:06






    • 2




      when one doesn't trust their own code - say, why do we make anything private in C#? Or final in Java? It annoys me to no end that the attitude is "bah, doesn't really matter" only when the language is VBA.
      – Mathieu Guindon
      Nov 16 at 18:34










    • Well, VBA is a long way from truly object oriented languages. For one it lacks a constructor accepting arguments. That would really be the way to go to initialise an instance, but it's not possible in VBA (unless I'm missing something), so we get into a kind of hacky way to almost get there. I'm not completely convinced that such workarounds are better than just living with the fact that VBA is not all that fantastic.
      – trincot
      Nov 16 at 21:12










    • Factories to the rescue - paired with a stateless default instance and a get-only interface, you get to do Dim aluminum As IMaterial: Set aluminum = Material.Create("AL", 169.34). Here's a whole Battleship game written in full-blown OOP, 100% VBA. Factory methods, adapter patterns, Model-View-Controller architecture... looks clean AF to me. the only thing VBA doesn't do is class inheritance.
      – Mathieu Guindon
      Nov 16 at 21:26













    up vote
    2
    down vote










    up vote
    2
    down vote









    You could create a Class module -- let's call it Material, and define the properties a material has as public members (variables), like Density, Symbol:



    Public Density As Float
    Public Symbol As String


    Then in a standard module create the materials:



    Public Aluminium As New Material
    Aluminium.Density = 169.34
    Aluminium.Symbol = "AL"

    Public Copper As New Material
    ' ... etc


    Adding behaviour



    The nice thing about classes is that you can define functions in it (methods) which you can also call with the dot notation on any instance. For example, if could define in the class:



    Public Function AsString() 
    AsString = Symbol & "(" & Density & ")"
    End Function


    ...then with your instance Aluminium (see earlier) you can do:



    MsgBox Aluminium.AsString() ' =>   "AL(169.34)"


    And whenever you have a new feature/behaviour to implement that must be available for all materials, you only have to implement it in the class.



    Another example. Define in the class:



    Public Function CalculateWeight(Volume As Float) As Float
    CalculateWeight = Volume * Density
    End Function


    ...and you can now do:



    Weight = Aluminium.CalculateWeight(50.6)


    Making the properties read-only



    If you want to be sure that your code does not assign a new value to the Density and Symbol properties, then you need a bit more code. In the class you would define those properties with getters and setters (using Get and Set syntax). For example, Symbol would be defined as follows:



    Private privSymbol as String

    Property Get Symbol() As String
    Symbol = privSymbol
    End Property

    Property Set Symbol(value As String)
    If privSymbol = "" Then privSymbol = value
    End Property


    The above code will only allow to set the Symbol property if it is different from the empty string. Once set to "AL" it cannot be changed any more. You might even want to raise an error if such an attempt is made.






    share|improve this answer














    You could create a Class module -- let's call it Material, and define the properties a material has as public members (variables), like Density, Symbol:



    Public Density As Float
    Public Symbol As String


    Then in a standard module create the materials:



    Public Aluminium As New Material
    Aluminium.Density = 169.34
    Aluminium.Symbol = "AL"

    Public Copper As New Material
    ' ... etc


    Adding behaviour



    The nice thing about classes is that you can define functions in it (methods) which you can also call with the dot notation on any instance. For example, if could define in the class:



    Public Function AsString() 
    AsString = Symbol & "(" & Density & ")"
    End Function


    ...then with your instance Aluminium (see earlier) you can do:



    MsgBox Aluminium.AsString() ' =>   "AL(169.34)"


    And whenever you have a new feature/behaviour to implement that must be available for all materials, you only have to implement it in the class.



    Another example. Define in the class:



    Public Function CalculateWeight(Volume As Float) As Float
    CalculateWeight = Volume * Density
    End Function


    ...and you can now do:



    Weight = Aluminium.CalculateWeight(50.6)


    Making the properties read-only



    If you want to be sure that your code does not assign a new value to the Density and Symbol properties, then you need a bit more code. In the class you would define those properties with getters and setters (using Get and Set syntax). For example, Symbol would be defined as follows:



    Private privSymbol as String

    Property Get Symbol() As String
    Symbol = privSymbol
    End Property

    Property Set Symbol(value As String)
    If privSymbol = "" Then privSymbol = value
    End Property


    The above code will only allow to set the Symbol property if it is different from the empty string. Once set to "AL" it cannot be changed any more. You might even want to raise an error if such an attempt is made.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 16 at 21:50

























    answered Nov 16 at 17:34









    trincot

    113k1477109




    113k1477109








    • 1




      this is a good suggestion but I think code could potentially set Aluminum.Density = 2 since I don't think it's really a constant
      – R. Binter
      Nov 16 at 17:46










    • Indeed, @R.Binter, when one doesn't trust their own code to not change it, then you can make it a getter/setter property that only allows one change to the property (for initialisation) and no more. I added the suggest code for that to my answer.
      – trincot
      Nov 16 at 18:06






    • 2




      when one doesn't trust their own code - say, why do we make anything private in C#? Or final in Java? It annoys me to no end that the attitude is "bah, doesn't really matter" only when the language is VBA.
      – Mathieu Guindon
      Nov 16 at 18:34










    • Well, VBA is a long way from truly object oriented languages. For one it lacks a constructor accepting arguments. That would really be the way to go to initialise an instance, but it's not possible in VBA (unless I'm missing something), so we get into a kind of hacky way to almost get there. I'm not completely convinced that such workarounds are better than just living with the fact that VBA is not all that fantastic.
      – trincot
      Nov 16 at 21:12










    • Factories to the rescue - paired with a stateless default instance and a get-only interface, you get to do Dim aluminum As IMaterial: Set aluminum = Material.Create("AL", 169.34). Here's a whole Battleship game written in full-blown OOP, 100% VBA. Factory methods, adapter patterns, Model-View-Controller architecture... looks clean AF to me. the only thing VBA doesn't do is class inheritance.
      – Mathieu Guindon
      Nov 16 at 21:26














    • 1




      this is a good suggestion but I think code could potentially set Aluminum.Density = 2 since I don't think it's really a constant
      – R. Binter
      Nov 16 at 17:46










    • Indeed, @R.Binter, when one doesn't trust their own code to not change it, then you can make it a getter/setter property that only allows one change to the property (for initialisation) and no more. I added the suggest code for that to my answer.
      – trincot
      Nov 16 at 18:06






    • 2




      when one doesn't trust their own code - say, why do we make anything private in C#? Or final in Java? It annoys me to no end that the attitude is "bah, doesn't really matter" only when the language is VBA.
      – Mathieu Guindon
      Nov 16 at 18:34










    • Well, VBA is a long way from truly object oriented languages. For one it lacks a constructor accepting arguments. That would really be the way to go to initialise an instance, but it's not possible in VBA (unless I'm missing something), so we get into a kind of hacky way to almost get there. I'm not completely convinced that such workarounds are better than just living with the fact that VBA is not all that fantastic.
      – trincot
      Nov 16 at 21:12










    • Factories to the rescue - paired with a stateless default instance and a get-only interface, you get to do Dim aluminum As IMaterial: Set aluminum = Material.Create("AL", 169.34). Here's a whole Battleship game written in full-blown OOP, 100% VBA. Factory methods, adapter patterns, Model-View-Controller architecture... looks clean AF to me. the only thing VBA doesn't do is class inheritance.
      – Mathieu Guindon
      Nov 16 at 21:26








    1




    1




    this is a good suggestion but I think code could potentially set Aluminum.Density = 2 since I don't think it's really a constant
    – R. Binter
    Nov 16 at 17:46




    this is a good suggestion but I think code could potentially set Aluminum.Density = 2 since I don't think it's really a constant
    – R. Binter
    Nov 16 at 17:46












    Indeed, @R.Binter, when one doesn't trust their own code to not change it, then you can make it a getter/setter property that only allows one change to the property (for initialisation) and no more. I added the suggest code for that to my answer.
    – trincot
    Nov 16 at 18:06




    Indeed, @R.Binter, when one doesn't trust their own code to not change it, then you can make it a getter/setter property that only allows one change to the property (for initialisation) and no more. I added the suggest code for that to my answer.
    – trincot
    Nov 16 at 18:06




    2




    2




    when one doesn't trust their own code - say, why do we make anything private in C#? Or final in Java? It annoys me to no end that the attitude is "bah, doesn't really matter" only when the language is VBA.
    – Mathieu Guindon
    Nov 16 at 18:34




    when one doesn't trust their own code - say, why do we make anything private in C#? Or final in Java? It annoys me to no end that the attitude is "bah, doesn't really matter" only when the language is VBA.
    – Mathieu Guindon
    Nov 16 at 18:34












    Well, VBA is a long way from truly object oriented languages. For one it lacks a constructor accepting arguments. That would really be the way to go to initialise an instance, but it's not possible in VBA (unless I'm missing something), so we get into a kind of hacky way to almost get there. I'm not completely convinced that such workarounds are better than just living with the fact that VBA is not all that fantastic.
    – trincot
    Nov 16 at 21:12




    Well, VBA is a long way from truly object oriented languages. For one it lacks a constructor accepting arguments. That would really be the way to go to initialise an instance, but it's not possible in VBA (unless I'm missing something), so we get into a kind of hacky way to almost get there. I'm not completely convinced that such workarounds are better than just living with the fact that VBA is not all that fantastic.
    – trincot
    Nov 16 at 21:12












    Factories to the rescue - paired with a stateless default instance and a get-only interface, you get to do Dim aluminum As IMaterial: Set aluminum = Material.Create("AL", 169.34). Here's a whole Battleship game written in full-blown OOP, 100% VBA. Factory methods, adapter patterns, Model-View-Controller architecture... looks clean AF to me. the only thing VBA doesn't do is class inheritance.
    – Mathieu Guindon
    Nov 16 at 21:26




    Factories to the rescue - paired with a stateless default instance and a get-only interface, you get to do Dim aluminum As IMaterial: Set aluminum = Material.Create("AL", 169.34). Here's a whole Battleship game written in full-blown OOP, 100% VBA. Factory methods, adapter patterns, Model-View-Controller architecture... looks clean AF to me. the only thing VBA doesn't do is class inheritance.
    – Mathieu Guindon
    Nov 16 at 21:26










    up vote
    0
    down vote













    I like a hybrid approach. This is pseudo code because I don't quite have the time to fully work the example.



    Create a MaterialsDataClass - see Mathieu Guindon's knowledge about setting this up as a static class



    Private ArrayOfSymbols() as String
    Private ArrayOfDensity() as Double
    Private ArrayOfName() as String
    ' ....

    ArrayOfSymbols = Split("H|He|AL|O|...","|")
    ArrayOfDensity = '....
    ArrayOfName = '....

    Property Get GetMaterialBySymbol(value as Variant) as Material
    Dim Index as Long
    Dim NewMaterial as Material
    'Find value in the Symbol array, get the Index
    New Material = SetNewMaterial(ArrayOfSymbols(Index), ArrayofName(Index), ArrayofDensity(Index))
    GetMaterialBySymbol = NewMaterial
    End Property

    Property Get GetMaterialByName(value as string) ' etc.


    Material itself is similar to other answers. I have used a Type below, but I prefer Classes over Types because they allow more functionality, and they also can be used in 'For Each' loops.



    Public Type Material
    Density As Double
    Symbol As String
    Name as String
    End Type


    In your usage:



    Public MaterialsData as New MaterialsDataClass
    Dim MyMaterial as Material
    Set MyMaterial = MaterialsDataClass.GetMaterialByName("Aluminium")
    Debug.print MyMaterial.Density





    share|improve this answer

























      up vote
      0
      down vote













      I like a hybrid approach. This is pseudo code because I don't quite have the time to fully work the example.



      Create a MaterialsDataClass - see Mathieu Guindon's knowledge about setting this up as a static class



      Private ArrayOfSymbols() as String
      Private ArrayOfDensity() as Double
      Private ArrayOfName() as String
      ' ....

      ArrayOfSymbols = Split("H|He|AL|O|...","|")
      ArrayOfDensity = '....
      ArrayOfName = '....

      Property Get GetMaterialBySymbol(value as Variant) as Material
      Dim Index as Long
      Dim NewMaterial as Material
      'Find value in the Symbol array, get the Index
      New Material = SetNewMaterial(ArrayOfSymbols(Index), ArrayofName(Index), ArrayofDensity(Index))
      GetMaterialBySymbol = NewMaterial
      End Property

      Property Get GetMaterialByName(value as string) ' etc.


      Material itself is similar to other answers. I have used a Type below, but I prefer Classes over Types because they allow more functionality, and they also can be used in 'For Each' loops.



      Public Type Material
      Density As Double
      Symbol As String
      Name as String
      End Type


      In your usage:



      Public MaterialsData as New MaterialsDataClass
      Dim MyMaterial as Material
      Set MyMaterial = MaterialsDataClass.GetMaterialByName("Aluminium")
      Debug.print MyMaterial.Density





      share|improve this answer























        up vote
        0
        down vote










        up vote
        0
        down vote









        I like a hybrid approach. This is pseudo code because I don't quite have the time to fully work the example.



        Create a MaterialsDataClass - see Mathieu Guindon's knowledge about setting this up as a static class



        Private ArrayOfSymbols() as String
        Private ArrayOfDensity() as Double
        Private ArrayOfName() as String
        ' ....

        ArrayOfSymbols = Split("H|He|AL|O|...","|")
        ArrayOfDensity = '....
        ArrayOfName = '....

        Property Get GetMaterialBySymbol(value as Variant) as Material
        Dim Index as Long
        Dim NewMaterial as Material
        'Find value in the Symbol array, get the Index
        New Material = SetNewMaterial(ArrayOfSymbols(Index), ArrayofName(Index), ArrayofDensity(Index))
        GetMaterialBySymbol = NewMaterial
        End Property

        Property Get GetMaterialByName(value as string) ' etc.


        Material itself is similar to other answers. I have used a Type below, but I prefer Classes over Types because they allow more functionality, and they also can be used in 'For Each' loops.



        Public Type Material
        Density As Double
        Symbol As String
        Name as String
        End Type


        In your usage:



        Public MaterialsData as New MaterialsDataClass
        Dim MyMaterial as Material
        Set MyMaterial = MaterialsDataClass.GetMaterialByName("Aluminium")
        Debug.print MyMaterial.Density





        share|improve this answer












        I like a hybrid approach. This is pseudo code because I don't quite have the time to fully work the example.



        Create a MaterialsDataClass - see Mathieu Guindon's knowledge about setting this up as a static class



        Private ArrayOfSymbols() as String
        Private ArrayOfDensity() as Double
        Private ArrayOfName() as String
        ' ....

        ArrayOfSymbols = Split("H|He|AL|O|...","|")
        ArrayOfDensity = '....
        ArrayOfName = '....

        Property Get GetMaterialBySymbol(value as Variant) as Material
        Dim Index as Long
        Dim NewMaterial as Material
        'Find value in the Symbol array, get the Index
        New Material = SetNewMaterial(ArrayOfSymbols(Index), ArrayofName(Index), ArrayofDensity(Index))
        GetMaterialBySymbol = NewMaterial
        End Property

        Property Get GetMaterialByName(value as string) ' etc.


        Material itself is similar to other answers. I have used a Type below, but I prefer Classes over Types because they allow more functionality, and they also can be used in 'For Each' loops.



        Public Type Material
        Density As Double
        Symbol As String
        Name as String
        End Type


        In your usage:



        Public MaterialsData as New MaterialsDataClass
        Dim MyMaterial as Material
        Set MyMaterial = MaterialsDataClass.GetMaterialByName("Aluminium")
        Debug.print MyMaterial.Density






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 16 at 21:20









        AJD

        1,2732313




        1,2732313






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53342482%2fconstant-with-dot-operator-vba%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            "Incorrect syntax near the keyword 'ON'. (on update cascade, on delete cascade,)

            Alcedinidae

            Origin of the phrase “under your belt”?