I'm trying to represent a human wearing glasses near a window when a nearby explosion occurs. main is a sketch of what should be done during the explosion. Something should gather up a list of objects in proximity of the explosion and do specific things (such as shattering or melting) for each of them. The glass and window shatter as expected, but for some reason the human also shatters. Why?
package main
import "fmt"
type Human struct { Glasses }
type Glasses struct {}
type Shatterable interface { shatter() }
func (g Glasses) shatter() {}
type Window struct {}
func (w Window) shatter() {}
func main() {
h := Human{Glasses{}}
objectsInProximity := []interface{}{h,h.Glasses,Window{}}
for _,o := range objectsInProximity {
shatter(o)
}
}
func shatter(i interface{}) {
s, ok := i.(Shatterable)
if ok {
fmt.Printf("shattering a %T\n", s)
s.shatter()
}
}
$ go run a.go
shattering a main.Human
shattering a main.Glasses
shattering a main.Window
As mentioned in this thread:
We're talking about anonymous fields of a struct
(http://golang.org/ref/spec#Struct_types).
(modified version, as the term superset is confusing)
A struct with an anonymous field satisfies every interface with all the interface methods declared by the anonymous field or the struct itself.
type Human struct { Glasses }
Since Glasses can be shattered, Human also satisfy the same interface.
Note: embedding anonymous field in a struct is the closest to "inheritance", even though the answer to "Golang: what's the point of interfaces when you have multiple inheritance" reminds us that:
Go doesn't have inheritance.
If Man 'extended' Human (by having it as an anonymous field), any method that used Human as an argument, would not be able to take Man as an argument.
(That means here Human extends... Glasses?! That might show some kind of design imperfection)
I explained before in "If struct A is embedded in B, can methods on A access method and fields of B?" that this isn't true sub-typing.
Interfaces enable functions to have a 'placeholder' parameter which can take different structs as an argument.
Here, if Human isn't supposed to be shattered, it shouldn't include the anonymous field Glasses.
Related
I'm new to golang and am trying to understand a code example of type wrapping for the "non-atomic" type time.Time.
The type extension in question is from the Go client for GDAX on github, go-coinbase-exchange project.
The expected behavior would be for Time variables from the project (coinbase.Time), which are of type Time time.Time (as defined in the project's time.go file) to behave something like the following example for extending the "atomic" type int (from blog.riff.org in that they might follow a kind of "inheritance" from the base type for functions like Time.format (from golang's standard implementation of time:
package main
import "fmt"
type Int int
func (i Int) Add(j Int) Int {
return i + j
}
func main() {
i := Int(5)
j := Int(6)
fmt.Println(i.Add(j))
fmt.Println(i.Add(j) + 12)
}
But if I modify the code example from the project's List Account Ledger example found in Readme.md to include a print function which might otherwise give me a human-readable view of the CreatedAt struct variables (as follows), I get a compiler error saying that "type coinbase.Time has no field or method Format":
for _, e := range ledger {
print("Entry Creation: ")
fmt.Printf(e.CreatedAt.Format("2006-01-02 15:04:05.999999+00"))
}
The expected behavior inside the for loop would be for it to print the ledger entries in a human-readable format. I can get the contents of the structs, but I'm not really sure how to then use the resulting wall, ext and loc members.
For example, inserting fmt.Printf("%#v", e.CreatedAt) into the for loop gives me a representation of the time that looks something like this:
coinbase.Time{wall:0x3015a123, ext:63612345678, loc:(*time.Location)(nil)}
{806986000 63638738354 <nil>}
I can also see that wall is of type uint64, that ext is of type int64 and that loc is just GMT/UTC=0 by formatting the variable as a string because fmt.Printf("%s", e.CreatedAt) gives me output which is similar to the following:
{%!s(uint64=712345678) %!s(int64=63612345678) %!s(*time.Location=<nil>)}
It seems like I'm missing something. I've requested further information through issues tab on github, but maybe this is a nube question. So I'm not sure how quick the response time would be, and I'm interested in the more general case for extending non-atomic types in go.
Named types do not inherit any methods from the underlying type (indeed there is no inheritance at all in Go); you must cast to the underlying type to call any methods from that type:
(time.Time(e.CreatedAt)).Format("2006-01-02 15:04:05.999999+00")
I used to do C++ development several years ago and back then I found it difficult to combine template programming with OOP. Currently I program in Swift and I tried doing some of the things I struggled with then.
This Swift code will illustrate the problem:
// protocol is like Java interface or C++ pure virtual base class
protocol Log {
// want to able to add elements from a collection of Ints, but
// it should be any sort of collection that
// can be treated as a sequence
func add<T: SequenceType where T.Generator.Element == Int>(values: T)
}
class DiscreteLog: Log {
var vals: [Int] = []
func add<T: SequenceType where T.Generator.Element == Int>(values: T) {
for v in values {
vals.append(v)
}
}
}
class ContinousLog: Log {
var vals: [Double] = []
func add<T: SequenceType where T.Generator.Element == Int>(values: T) {
for v in values {
vals.append(Double(v))
}
}
}
// I don't have to know whether the log is Continuous or Discrete
// I can still add elements to it
var log: Log = ContinousLog()
log.add([1, 2, 3])
// and elements can come from any kind of sequence, it does not need
// to be an array
log.add(["four": 4, "five: 5].values)
So the problem is that if the C++ code defined as as:
virtual void add(vector<Int> elements>)
Then sure I could have multiple subclasses implement this method, but I could never provide anything but vectors as arguments.
I could try changing it to something more generic using iterator:
virtual void add(vector<Int>::iterator elements>)
But I am still limited to using vector iterators. So I guess I would have to write something like:
template<typename Iterator>
virtual void add(Iterator elements>)
But that will give compile errors as template based arguments are not allowed for virtual methods.
Anyway I wondered if this sort of thing is possible in modern C++.
C++ templates and C#/Swift/Java generics are different things.
They are both "pattern code" in a sense (they are patterns that generate code), but C#/Swift/Java generics use type erasure and "forget" almost everything about the types they work with, while C++ templates are elephants. And elephants never forget.
It turns out that can make an elephant forget, but you have to tell it to. The technique of "forgetting" about details of a type is known as "type erasure" or "run time concepts".
So you want to type erase down to the concept of "a sequence of integers". You want to take any type, so long as it is a sequence of integers, and be able to iterate over it. Seems fair.
boost has such type erasures. But who wants to always rely on boost?
First, type erase an input iterator:
template<class T>
struct input_iterator:
std::iterator<
std::input_iterator_tag, // category
T, // value
std::ptrdiff_t, // distance
T*, // pointer
T // reference
>
{
struct erase {
virtual void advance() = 0;
virtual erase* clone() const = 0;
virtual T get() const = 0;
virtual bool equal(erase const& o) = 0;
virtual ~erase() {}
};
std::unique_ptr<erase> pimpl;
input_iterator(input_iterator&&)=default;
input_iterator& operator=(input_iterator&&)=default;
input_iterator()=default;
input_iterator& operator++() {
pimpl->advance();
return *this;
}
input_iterator operator++(int) {
auto copy = *this;
++*this;
return copy;
}
input_iterator(input_iterator const& o):
pimpl(o.pimpl?o.pimpl->clone():nullptr)
{}
input_iterator& operator=(input_iterator const&o) {
if (!o.pimpl) {
if (pimpl) pimpl->reset();
return *this;
}
pimpl = std::unique_ptr<erase>(o.pimpl->clone());
return *this;
}
T operator*() const {
return pimpl->get();
}
friend bool operator==( input_iterator const& lhs, input_iterator const& rhs ) {
return lhs.pimpl->equal(*rhs.pimpl);
}
friend bool operator!=( input_iterator const& lhs, input_iterator const& rhs ) {
return !(lhs==rhs);
}
template<class It>
struct impl:erase{
It it;
impl(impl const&)=default;
impl(It in):it(std::move(in)){}
virtual void advance() override { ++it; }
virtual erase* clone() const override { return new impl(*this); }
virtual T get() const override { return *it; }
virtual bool equal(erase const& o) override {
return static_cast<impl const&>(o).it == it;
}
};
template<
class It,
class=std::enable_if<
std::is_convertible<
typename std::iterator_traits<It>::reference,
T
>{}
>
>
input_iterator(It it):pimpl( new impl<It>{it} ) {}
}; // input_iterator
Next, have a range template. This is a container that stores non-type erased iterators, and exposes enough to iterate over those iterators.
template<class It>
struct range {
It b; It e;
It begin() const { return b; }
It end() const { return e; }
range() = default;
range(It start, It finish):b(std::move(start)),e(std::move(finish)) {};
range(range&&)=default;
range(range const&)=default;
range& operator=(range&&)=default;
range& operator=(range const&)=default;
template<class R,
class R_It=std::decay_t<decltype(std::begin(std::declval<R>()))>,
class=std::enable_if< std::is_convertible<R_It, It>{} >
>
range( R&& r ):
range(std::begin(r), std::end(r))
{} // TODO: enable ADL begin lookup
};
The above type is really basic: C++1z has better ones, as does boost, as do I have in my own code base. But it is enough to handle for(:) loops, and implicit conversion from containers with compatible iterators.
Finally our sequence type:
template<class T>
using sequence_of = range<input_iterator<T>>;
Wait, that's it? Nice, those types compose well!
And barring errors, we are done.
Your code now would take a sequence_of<int>, and they could pass a std::vector<int> or std::list<int> or whatever.
The input_iterator type-erasure type-erases any iterator down to getting a T via *, ==, copy, and ++ advance, which is enough for a for(:) loop.
The range<input_iterator<int>> will accept any iterable range (including containers) whose iterators can be converted to an input_iterator<int>.
The downside? We just introduced a bunch of overhead. Each method goes through virtual dispatch, from ++ to * to ==.
This is (roughly) what generics do -- they type-erase down to the requirements you give it in the generic clause. This means they are working with abstract objects, not concrete objects, so they unavoidably suffer performance penalties of this indirection.
C++ templates can be used to generate type erasure, and there are even tools (boost has some) to make it easier. What I did above is a half-assed manual one. Similar techniques are used in std::function<R(Args...)>, which type-erases down to (conceptually) {copy, call with (Args...) returning R, destroy} (plus some incidentals).
live example.
(The code above freely uses C++14.)
So the C++ equivalent Log is:
struct Log {
virtual void add(sequence_of<int>) = 0;
virtual ~Log() {}
};
Now, the type erasure code above is a bit ugly. To be fair, I just implemented a language feature in C++ without direct language support for it.
I've seen some proposals to make type erasure easier in C++. I do not know the status of those proposals.
If you want to do your own, here is an "easy" way to do type erasure in 3 steps:
First, determine what operations you want to erase. Write the equivalent of input_iterator<T> -- give it a bunch of methods and operators that do what you want. Be sparse. Call this the "external type". Ideally nothing in this type is virtual, and it should be a Regular or Semi-regular type (ie, it should behave value-like, or move-only-value-like). Don't implement anything but the interface yet.
Second, write an inner class erase. It provides a pure-virtual interface to a set of functions that could provide what you need in your external type.
Store a unique_ptr<erase> pimpl; within the external type. Forward the methods you expose in the external type to the pimpl;.
Third, write an inner template<class X> class impl<X>:erase. It stores a variable X x;, and it implements everything in erase by interacting with X. It should be constructable from an X (with optional perfect forwarding).
You then create a perfect forwarding constructor for the external type that creates its pimpl via a new impl<X>(whatever). Ideally it should check that its argument is a valid one via SFINAE techniques, but that is just a qualify of implementation issue.
Now the external type "erases" the type of any object it is constructed from "down to" the operations you exposed.
Now, for your actual problem, I'd write array_view or steal std::experimental::array_view, and restrict my input to be any kind of contiguous buffer of data of that type. This is more performant, and accepting any sequence is over engineering unless you really need it.
I want to implement a collision library using the interface Collidable
type Collidable interface{
BoundingBox() (float64,float64,float64,float64)
FastCollisionCheck(c2 Collidable) bool
DoesCollide(c2 Collidable) bool
Collide(c2 Collidable)
}
It has predefined shapes like.
type Circle struct{
X,Y,Radius float64
}
The idea is that I can do
type Rock struct{
collision.Circle
....
}
which then implements the interface Collidable, so I can pass it to a Spatial Hash Map (that expects a collidable). Only thing needed to do would be to override the Collide() function to my needs.
However the functions in type circle can not handle the type rock, even tough it has a circle embedded.
func (c1 *Circle) DoesCollide(i Collidable) bool{
switch c2 := value.(type) {
case Circle:
//doesn't fire, as it is of type Rock (unknown in this package)
//Needed is something like
//if i_embeds_Circle then c2 := i_to_Circle
}
}
Is this possible?
Is there a better way?
You are trying to use an object oriented design pattern with inheritance. This is not how to do this in Go. Beside, interface names end in 'able' in Java or equivalent object oriented languages. In Go the convention is to end interface names with 'er'.
To answer your question about the Rock, I would suggest that all thing that can collide into another thing implements the method CollisonShape() returning a collison.Shaper (e.g. Circle) that you will use to test collison. Here collison is the name of your package.
// This interface is defined in the collison package.
// Any object that may collide must implement that method.
type Collider interface {
CollisonShape() Shaper
}
// This function defined in the collison package
// test if two Collider collide into each other.
func Collide(c1, c2 Collider) bool {
shape1, shape2 := c1.CollisonShape(), c2.CollisonShape()
...
}
// This is how you would define an object that can collide.
type Rock struct {
shape *collison.Circle
...
}
// Implements the Collider interface.
// The return type must be the same as in the interface.
func (r *Rock) CollisonShape() collison.Shaper {
return r.shape
}
As you see, we use a method to access the collison shape of the rock. This allow us to write
if collison.Collide(rock, spaceCraft) {...}
This answer your question on how to do get the collison Shape of Rock.
If you want to avoid the call to the CollisonShape() method in the Collide() function, you will have to pass directly the collison.Shaper.
The method Collide would be defined in the collison package as
func Collide(shape1, shape2 Shaper) bool {...}
You then would have to write
if collison.Collide(rock.shape, spacecraft.shape) {...}
This design would be slightly more efficient, but the price to pay is less readable code which is frown upon by experienced Go programmers.
If you want Circle to be an embedded struct in rock you would then have to define it the following way. Embedding the shape saves allocation time for the Circle and some work for the GC.
type Rock struct {
shape collison.Circle
....
}
if collison.Collide(&rock.shape, &spacecraft.shape) {...}
If you want to use an anonymous embedded struct, you then would have to write
type Rock struct {
Circle
....
}
if collison.Collide(&rock.Circle, &spacecraft.Rectangle) {...}
As you see, the code gets less and less readable, and less convenient to use. The shape is not abstracted anymore. Using anonymous embedded struct should be limited to the very few cases where it really make sense.
By using the initially suggested CollisonShape() method, you could easily change your Rock structure into this one without breaking any code.
type Rock struct {
shape collison.Circle
...
}
func (r *Rock) CollisonShape() collison.Shaper {
return &r.shape
}
This now make the shape and embedded struct. The use of a method to get the shape decouples the internal implementation of Rock from the access to the shape. You can change the internal implementation of Rock without needing to change code in other places.
This is one of the reason why Go doesn't support inheritence. It creates a very strong dependency and coupling between the base class and derived classes. Experience has shown that people often regret such coupling as the code evolves. Object composition is preferred and recommended and well supported by Go.
If efficiency is your goal, every Collider should have one position which changes and a bounding box with a width and height that doesn't change. You can save a few operations using these values for the bounding box overlap test. But this is another story.
Not sure how off the mark this is, but posting it in case it helps you in any way.
http://play.golang.org/p/JYuIRqwHCm
If you are calling a legacy library which is not extensible (and cannot detect Rock, only Circle), there seems to be only one solution, pass a Circle.
See this example, where I use a non-anonymous field 'c' for Rock
(That means Rock has to implement the interface, which is a simple delegation to Circle.Shape())
type Shaper interface {
Shape()
}
type Circle struct{}
func (c *Circle) Shape() {}
type Rock struct{ c *Circle }
func (r *Rock) Shape() { r.Shape() }
func DoesShape(s Shaper) {
fmt.Println("type:", reflect.TypeOf(s))
switch st := s.(type) {
case *Circle:
fmt.Println("Shaper Circle %+v", st)
default:
fmt.Println("Shaper unknown")
}
}
func main() {
c := &Circle{}
DoesShape(c)
r := &Rock{c}
DoesShape(r.c)
}
Then the output is:
type: *main.Circle
Shaper Circle %+v &{}
type: *main.Circle
Shaper Circle %+v &{}
I have solved a similar problem by slightly modifying the base class. Not sure whether this is the solution you were looking for.
import "fmt"
type IShaper interface {
Shape()
}
type Rect struct{}
func (r *Rect) Shape() {}
type Circle struct{}
func (c *Circle) GetCircle() *Circle { return c }
func (c *Circle) Shape() {}
type Rock struct{ *Circle }
type ShapeWithCircle interface {
GetCircle() *Circle
}
func DoesShape(s IShaper) {
if sc, ok := s.(ShapeWithCircle); ok {
fmt.Printf("Shaper Circle %+v\n", sc.GetCircle())
} else {
fmt.Println("Shaper unknown")
}
}
func main() {
DoesShape(&Circle{})
DoesShape(&Rock{&Circle{}})
DoesShape(&Rect{})
}
That is, add a trivial function GetCircle() so that anything embedding a Circle has a function to get the (possibly embedded) circle. Then anybody needing a circle can trivially write an interface (ShapeWithCircle here) allowing one to test whether GetCircle() is defined and, if so, call it to get the embedded circle.
Play with it at https://play.golang.org/p/IDkjTPrG3Z5 .
The following interface defines a set of methods to be implemented by mooing objects:
type Mooing interface {
Moo() string
}
The following defines a set of methods to be implemented by grazing objects:
type Grazing interface {
EatGrass()
}
I have a function that operates on cows:
func Milk(cow *Cow)
It doesn't have to be a cow, though--anything that conforms to Mooing and Grazing is close enough. In Go, is it possible to specify a parameter of Mooing and Grazing? In pseudocode, something like the following?
func Milk(cow {Mooing, Grazing})
In other words, only parameters that satisfy both of these interfaces will be accepted.
You can compose interfaces in Go as follows:
type MooingAndGrazing interface {
Mooing
Grazing
}
If you don't want to declare a new named type, you could inline this as:
func Milk(cow interface{Mooing; Grazing})
You can experiment with this example here: http://play.golang.org/p/xAODkd85Zq
I am starting studying OOP and I want to learn what constitutes a class. I am a little confused at how loosely some core elements are being used and thus adding to my confusion.
I have looked at the C++ class, the java class and I want to know enough to write my own pseudo class to help me understand.
For instance in this article I read this (.. class attribute (or class property, field, or data member)
I have seen rather well cut out questions that show that there is a difference between class property and class field for instance What is the difference between a Field and a Property in C#?
Depending on what language I am studying, is the definition of
Property
Fields
Class variables
Attributes
different from language to language?
"Fields", "class variables", and "attributes" are more-or-less the same - a low-level storage slot attached to an object. Each language's documentation might use a different term consistently, but most actual programmers use them interchangeably. (However, this also means some of the terms can be ambiguous, like "class variable" - which can be interpreted as "a variable of an instance of a given class", or "a variable of the class object itself" in a language where class objects are something you can manipulate directly.)
"Properties" are, in most languages I use, something else entirely - they're a way to attach custom behaviour to reading / writing a field. (Or to replace it.)
So in Java, the canonical example would be:
class Circle {
// The radius field
private double radius;
public Circle(double radius) {
this.radius = radius;
}
// The radius property
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
// We're doing something else besides setting the field value in the
// property setter
System.out.println("Setting radius to " + radius);
this.radius = radius;
}
// The circumference property, which is read-only
public double getCircumference() {
// We're not even reading a field here.
return 2 * Math.PI * radius;
}
}
(Note that in Java, a property foo is a pair of accessor methods called getFoo() and setFoo() - or just the getter if the property is read-only.)
Another way of looking at this is that "properties" are an abstraction - a promise by an object to allow callers to get or set a piece of data. While "fields" etc. are one possible implementation of this abstraction. The values for getRadius() or getCircumference() in the above example could be stored directly, or they could be calculated, it doesn't matter to the caller; the setters might or might not have side effects; it doesn't matter to the caller.
I agree with you, there's a lot of unnecessary confusion due to the loose definitions and inconsistent use of many OO terms. The terms you're asking about are used somewhat interchangeably, but one could say some are more general than others (descending order): Property -> Attributes -> Class Variables -> Fields.
The following passages, extracted from "Object-Oriented Analysis and Design" by Grady Booch help clarify the subject. Firstly, it's important to understand the concept of state:
The state of an object encompasses all of the (usually static) properties of the object plus the current (usually dynamic) values of each of these properties. By properties, we mean the totality of the object's attributes and relationships with other objects.
OOP is quite generic regarding certain nomenclature, as it varies wildly from language to language:
The terms field (Object Pascal), instance variable (Smalltalk), member object (C++), and slot (CLOS) are interchangeable, meaning a repository for part of the state of an object. Collectively, they constitute the object's structure.
But the notation introduced by the author is precise:
An attribute denotes a part of an aggregate object, and so is used during analysis as well as design to express a singular property of the class. Using the language-independent syntax, an attribute may have a name, a class, or both, and optionally a default expression: A:C=E.
Class variable: Part of the state of a class. Collectively, the class variables of a class constitute its structure. A class variable is shared by all instances of the same class. In C++, a class variable is declared as a static member.
In summary:
Property is a broad concept used to denote a particular characteristic of a class, encompassing both its attributes and its relationships to other classes.
Attribute denotes a part of an aggregate object, and so is used during analysis as well as design to express a singular property of the class.
Class variable is an attribute defined in a class of which a single copy exists, regardless of how many instances of the class exist. So all instances of that class share its value as well as its declaration.
Field is a language-specific term for instance variable, that is, an attribute whose value is specific to each object.
I've been doing oop for more than 20 years, and I find that people often use different words for the same things. My understanding is that fields, class variables and attributes all mean the same thing. However, property is best described by the stackoverflow link that you included in your question.
Generally fields, methods, static methods, properties, attributes and class (or static variables) do not change on a language basis... Although the syntax will probably change on a per language basis, they will be function in the way you would expect across languages (expect terms like fields/data members to be used interchangably across languages)
In C#....
A field is a variable that exists for a given instance of a class.
eg.
public class BaseClass
{
// This is a field that might be different in each instance of a class
private int _field;
// This is a property that accesses a field
protected int GetField
{
get
{
return _field;
}
}
}
Fields have a "visibility" this determines what other classes can see the field, so in the above example a private field can only be used by the class that contains it, but the property accessor provides readonly access to the field by subclasses.
A property lets you get (sometimes called an accessor) or set (sometimes called a mutator) the value of field... Properties let you do a couple of things, prevent writing a field for example from outside the class, change the visibility of the field (eg private/protected/public). A mutator allows you to provide some custom logic before setting the value of a field
So properties are more like methods to get/set the value of a field but provide more functionality
eg.
public class BaseClass
{
// This is a field that might be different in each instance of a class
private int _field;
// This is a property that accesses a field, but since it's visibility
// is protected only subclasses will know about this property
// (and through it the field) - The field and property in this case
// will be hidden from other classes.
protected int GetField
{
// This is an accessor
get
{
return _field;
}
// This is a mutator
set
{
// This can perform some more logic
if (_field != value)
{
Console.WriteLine("The value of _field changed");
_field = value;
OnChanged; // Call some imaginary OnChange method
} else {
Console.WriteLine("The value of _field was not changed");
}
}
}
}
A class or static variable is a variable which is the same for all instances of a class..
So, for example, if you wanted a description for a class that description would be the same for all instance of the class and could be accessed by using the class
eg.
public class BaseClass
{
// A static (or class variable) can be accessed from anywhere by writing
// BaseClass.DESCRIPTION
public static string DESCRIPTION = "BaseClass";
}
public class TestClass
{
public void Test()
{
string BaseClassDescription = BaseClass.DESCRIPTION;
}
}
I'd be careful when using terminology relating to an attribute. In C# it is a class that can be applied to other classes or methods by "decorating" the class or method, in other context's it may simply refer to a field that a class contains.
// The functionality of this attribute will be documented somewhere
[Test]
public class TestClass
{
[TestMethod]
public void TestMethod()
{
}
}
Some languages do not have "Attributes" like C# does (see above)
Hopefully that all makes sense... Don't want to overload you!
Firstly, you need to select a language. For example, I would recommend you to select Ruby language and community. Until you select a language, you cannot escape confusion, as different communities use different terms for the same things.
For example, what is known as Module in Ruby, Java knows as abstract class. What is known as attributes in some languages, is known as instance variables in Ruby. I recommend Ruby especially for its logical and well-designed OOP system.
Write the following in a *.rb file, or on the command line in irb (interactive Ruby interpreter):
class Dog # <-- Here you define a class representing all dogs.
def breathe # <-- Here you teach your class a method: #breathe
puts "I'm breathing."
end
def speak # <-- Here you teach your class another method: #speak
puts "Bow wow!"
end
end
Now that you have a class, you can create an instance of it:
Seamus = Dog.new
You have just created an instance, a particular dog of class Dog, and stored it in the constant Seamus. Now you can play with it:
Seamus.breathe # <-- Invoking #breathe instance method of Seamus
#=> I'm breathing.
Seamus.speak # <-- Invoking #speak instance method of Seamus
#=> Bow wow!
As for your remaining terminology questions, "property" or "attribute" is understood as "variable" in Ruby, almost always an instance variable. And as for the term "data member", just forget about it. The term "field" is not really used in Ruby, and "class variable" in Ruby means something very rarely used, which you definitely don't need to know at this moment.
So, to keep the world nice and show you that OOP is really simple and painless in Ruby, let us create an attribute, or, in Ruby terminology, an instance variable of Dog class. As we know, every dog has some weight, and different dogs may have different weights. So, upon creation of a new dog, we will require the user to tell us dog's weight:
class Dog
def initialize( weight ) # <-- Defining initialization method with one argument 'weight'
#weight = weight # <-- Setting the dog's attribute (instance variable)
end
attr_reader :weight # <-- Making the dog's weight attribute visible to the world.
end
Drooly = Dog.new( 16 ) # <-- Weight now must provide weight upon initialization.
Drooly.weight # <-- Now we can ask Drooly about his weight.
#=> 16
Remember, with Ruby (or Python), things are simple.
I discovered in my question that Properties as defined in .Net are just a convenience syntax for code, and they are not tied to underlying variables at all (except for Auto-Implemented Properties, of course). So, saying "what is the difference between class property and class field" is like saying: what is the difference between a method and an attribute. No difference, one is code and the other is data. And, they need not have anything to do with each other.
It is really too bad that the same words, like "attribute" and "property", are re-used in different languages and ideologies to have starkly different meanings. Maybe someone needs to define an object-oriented language to talk about concepts in OOP? UML?
In The Class
public class ClassSample
{
private int ClassAttribute;
public int Property
{
get { return ClassAttribute; }
set { ClassAttribute = value; }
}
}
In the Program
class Program
{
static void Main(string[] args)
{
var objectSample = new ClassSample();
//Get Object Property
var GetProperty = objectSample.Property;
}
}