I understood that it is possible to make a class Inheritance with the method “Class1 < Class 2”
For modules I must use the “Include Module” or “Extend Module” method.
I would now like to have access to the methods of a module and a class at the same time in a new class to display the results included in two variables:
module JulienDufren
class Class1
def self.variable1
a = 7
if a > 2
@value1 = 10
else
@value1 = 20
end
end
end
module Module1
def self.variable2
a = 0
if a > 2
@value2 = 10
else
@value2 = 20
end
end
end
class Class2
# How to retrieve @value1 and @value2?
end
end
How to retrieve @value1 and @value2 as simply as possible?
I found a solution but it seems to me too complex to be the best:
module JulienDufren
class Class1
def self.variable1(val1)
a = 8
if a > 2
@value1 = 10
else
@value1 = 20
end
val1 << @value1
end
end
module Module1
def self.variable2(val2)
a = 0
if a > 2
@value2 = 10
else
@value2 = 20
end
val2 << @value2
end
end
class Class2
def self.gets
val1 = []
Class1.variable1(val1)
p val1[0]
val2 = []
Module1.variable2(val2)
p val2[0]
end
gets
end
end
You were using a class variable in Class1, not an instance variable. Classes exist for being instantiated. If you never instantiate them, you could use a module instead.
Note also that you should now rename variable1 (and 2) to method1 or function1 because it is not at all a variable, and misnaming things makes it confusing for everyone. A variable stores data (and Ruby calls them “reference” which points to the data) but does not have any statements that do an action.
In Ruby, attributes (@value1, @value2) are always private. The only way to access them from outside is not to make them public but to add a public accessor method that sets or gets the value.
In your example, that would mean adding a module function to Module1 (analog a class method to Class1). Naively, that would look like:
module Module1
def self.value2
return @value2
end
# If you want other code to modify the value:
# def self.value2=(new_value)
# @value2 = new_value
# end
end
Since this is a lot of boilerplate, you instead want to let Ruby generate these methods automatically (metaprogramming). Better use this:
module Module1
attr_reader :value2
# or:
# attr_writer :value2
# or for both:
# attr_accessor :value2
end
For classes with class variables, it is:
class Class1
class << self
attr_reader :value1
end
end
Assuming your class had an instance variable@value3, it is:
class Class1
attr_reader :value3
end
The advantage of getter methods is:
Flexibility: You can preserve the method’s interface unchanged (that other code already uses) but update its implementation for example if the attribute has been renamed, its type has been changed etc.
Encapsulation: Other code can by mistake or maliciously modify the object that it gets from the getter and then your class may behave differently or throw errors. Having a getter method, you could safe-guard against this by returning a copy of the attribute’s object.
If you have a setter method, other code can by mistake or maliciously set an invalid or incompatible object, which may break your class. By having a setter method, you can add code in it that validates the assigned value.
With attributes in a module, it can be used as a mixin module, and with attributes in a superclass the subclass can inherit them.
module JulienDufren
module Module1
attr_accessor :value1
end
class Class1
class << self
attr_accessor :value2 # class variable
end
attr_accessor :value3 # instance variable
def initialize
self.class.value2= 2.0
@value3 = 3.0
end
end
class Class2 < Class1
include Module1
def initialize
super # calls the superclass' initializer
@value1 = 1.0
end
def get
puts "@value1 : #{value1()}"
puts "@value2 : #{self.class.value2()}"
puts "@value3 : #{value3()}"
end
end
end
Notice how (in the above example,) Module1 is not mixed into Class1.
This means that instances of it will not have access to the @value1 attribute (instance method,) and it does not have getter or setter methods inherited by proxy.
But instances ofClass2will have access to the @value1 attribute and be able to use the accessor methods value1() and value=() because Module1 gets inserted into Class2’s ancestry chain as a pseudo superclass when using include().
They will also have access to Class1’s class attribute @value2 via the class accessor methods and instance attribute @value3 via the instance accessor methods that it inherits by being a subclass.
NOTE ALL instance and class variables should be initialized before they are accessed. This is usually done in the class constructor, ie, the initialize() method. If you do not initialize them, they point at nil, and can also cause a warning to clutter the console if the value is attempted to be read.
Also, keep in mind that class variables (@value2 above) will be shared by all instances of Class1, Class2 and any subsequent subclasses created later. If one instance of Class2 changes the value, it changes for all as this is a “family attribute”.
Despite a short period of study on the Class and the Modules, I admit that I have a little trouble understanding all your information.
For the Class I will use “attr_accessor” as Aérilius advised me.
For the modules I will use a hash to allow me to register several local variables.
Currently my example code looks like this:
module JulienDufren
class Class1
attr_accessor :value1
def methode1
a = 8
if a > 2
@value1 = 10
else
@value1 = 20
end
end
end
module Module1
def self.methode2(hash)
a = 0
if a > 2
hash[:value2] = 10
else
hash[:value2] = 20
end
end
end
class Class2
class1 = Class1.new
class1.methode1
p "VALUE 1 = #{class1.value1}"
hash = {}
Module1.methode2(hash)
p "VALUE 2 = #{hash[:value2]}"
end
end
If you want access to Class1’s methods from Class2, just make the latter a subclass.
Again, instance variables now need to be initialized. This is nest done in the initialize method of the class, which executes in the new instance’s context.
Believe me, I really want to have good practices from the start of my apprenticeship.
I often post my codes on this forum to receive constructive feedback.
If I don’t apply everything I’ve been told the first time, it’s because I haven’t been able to understand what has been said to me.
Here is an example of module and class organization that apply what I understood on various tutorials and on this forum:
module JulienDufren
module UserObserver
class LanguageObserver
attr_accessor :language
def initialize(lang)
@language = lang
end
def user_language_methode
@language = Sketchup.os_language
end
end
class UnityObserver
attr_accessor :unitie
def initialize(uni)
@unitie = uni
end
def user_unity_methode
@unitie = "INCHES"
end
end
end
module MyPlugin
def self.user_language
langObserver = UserObserver::LanguageObserver.new("")
langObserver.user_language_methode
@language = langObserver.language
end
def self.user_unity
unityObserver = UserObserver::UnityObserver.new("")
unityObserver.user_unity_methode
@unity = unityObserver.unitie
end
def self.user_information
user_language
user_unity
p "USER LANGUAGE = #{@language}"
p "USER UNITY = #{@unity}"
end
user_information
end
end
I believe it would be helpful if you would define a specific usecase.
What would you like to achieve? Once you have that defined it is easier for us to guide you through the path on how to get there.
An observer is an object (class or module) that watches something else and reacts to changes.
The modules and classes in this example are not observers.
This is true rule. For example …
class LanguageObserver
attr_accessor :language
def initialize(lang)
@language = lang
end
def user_language_methode
@language = Sketchup.os_language
end
end
… is a perfect example of what NOT to do in object oriented programming.
Meaning, you do not need an entire class to encapsulate the setting of a language reference.
A simple @langauge variable can be used, or your plugin submodule can hold a hash of settings.
But the idea of saving a reference to the user language is unnecessary as it cannot be changed by the API and is already set by the time any extension loads. Your code can get the value at any time by calling the Sketchup.os_language module method.
Anyway, back to main point about object oriented programming. Do not waste time coding classes that hold state if a simple data structure like an Hash, OpenStruct, or a Struct will do. Otherwise your just wasting your own time and complicating your code.
I understand what you mean Dan.
But the purpose of this class is not only to determine the language of the user!
Many methods will be added to modify the language of the Plugin as well as the units of measurement.
My goal is to know how to properly organize my classes and my modules!