Jump to content
  • Advertisement
Sign in to follow this  
brainscrape

"Object Only" Languages and Free Functions

This topic is 3607 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

So everyone and their dog has read at least one article on How Non-Member Functions Improve Encapsulation. The idea is to narrow interfaces, improve encapsulation, blah blah. This is great for C++, but unfortunately there's this crazy fad with "everything is an object" languages these days--Java, C#, Actionscript 3.0, etc. How do people (who agree with those articles) try to follow the advice presented? Or do they? In many cases people will draw parallels between static functions of the same class and free functions living in the same namespace as that class. As the article mentions, though, static member functions are just as bad as non-static member functions in this particular case:
using System;

public class SomeObject
{
    public SomeObject(int value) { this.someVariable = value; }

    public static void printValue(SomeObject o)
    {
        // o's privates are visible... how embarassing
        Console.Out.WriteLine("Private member variable: " + o.someVariable);
    }

    private int someVariable;
}

public class Program
{
    static void Main(string[] args)
    {
        SomeObject foo = new SomeObject(3);
        SomeObject.printValue(foo);
    }
}

Of course a second static-methods-only class could be used to avoid this problem:
using System;

namespace SomeObjectStuff
{
    public class SomeObject
    {
        public SomeObject(int value) { this.someVariable = value; }

        private int someVariable;
    }

    public class SomeObjectFunctions
    {
        public static void printValue(SomeObject o)
        {
            // ERROR, Privates no longer visible!
            Console.Out.WriteLine(o.someVariable);
        }
    }
}

public class Program
{
    static void Main(string[] args)
    {
        SomeObjectStuff.SomeObject foo = new SomeObjectStuff.SomeObject(3);
        SomeObjectStuff.SomeObjectFunctions.printValue(foo);
    }
}

C# is used here as this is practically a C# fetish site, but I'm asking for a more general case solution here (C# isn't the language I'm using at the moment). Is this how it's generally dealt with, or are there some flaws with this approach? If there are, how is it usually done? Or is it generally seen as trying to force a feature down the language's throat, one that it almost seems to explicitly try to avoid?

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by brainscrape
How do people (who agree with those articles) try to follow the advice presented? Or do they?

None of the Java guys I know understand that philosophy. "Methods not part of the object? Why would you do that? That's not OO. Crazy C++ folks!"

Share this post


Link to post
Share on other sites
I read an interview with Bjarne a while back where he advocated the same kind of thing.

In C# (because that's my day job) I typically make them out of helper classes, so the functions necessary to return valid data about the wombat are in the wombat class and functions that merely use data about the wombat to draw other conclusions (like calculating its age from a given birth date) go in helper classes, whether static or not just depends on whether I need to keep state about the helper. Granted I don't follow this rule 100%, but it makes sense so I'm trying to.

Btw, are you suggesting that GameDev.net is a C# fetish site? If so I'd have to strongly disagree!

Share this post


Link to post
Share on other sites
The thing is that C++ is not an OOP language. C++ is a multi-paradigm language which supports some OOP or OOP-like features in ways which are generally inferior to the ways in which they are implemented in first-class OOP languages.

Most "pure" OOP languages don't have to make up for the shortcomings in C++ that are at the root of this idiom, most, simply put, have a better way built into the language. In other words, what is often a best practice for OOP in C++, doesn't even apply to other languages. Its a case of apples to oranges.

Share this post


Link to post
Share on other sites
Try this:

public abstract class AbstractObject
{
public abstract string ValueAsString();
public static void PrintValue(AbstractObject obj)
{
// Error, no such member "obj.value"
Console.Out.WriteLine("Private member variable: " + obj.value);

// Works fine
Console.Out.WriteLine("My value: " + obj.ValueAsString();
}
}

public class SomeObject : public AbstractObject
{
private int value;
public SomeObject(int value) { this.value = value; }
public override string ValueAsString()
{
return this.value.ToString();
}
}



Or the equivalent based on an interface (which delegates the appropriate functions to the appropriate objects).

Share this post


Link to post
Share on other sites
Quote:
Original post by Ravyne
The thing is that C++ is not an OOP language. C++ is a multi-paradigm language which supports some OOP or OOP-like features in ways which are generally inferior to the ways in which they are implemented in first-class OOP languages.


You have to be careful with value-statements like this - calling C++ "inferior" to what you consider first-class OOP languages. OOP is not a silver bullet, and "pure OOP" only limits language expressiveness for no good reason. What would you consider a first-class OOP language? Java and C# "pepper classes with static methods because you don't have free functions"? I'd hope not.

You know what they say - if all you have is a hammer, everything looks like a nail.



Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Or the equivalent based on an interface (which delegates the appropriate functions to the appropriate objects).


This.

And it's less of a problem (or rather, the interface usage is a natural side effect) if you stick with the single responsibility principle.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ravyne
C++ is a multi-paradigm language which supports some OOP or OOP-like features in ways which are generally inferior to the ways in which they are implemented in first-class OOP languages.

Inferior from the POV of the pure OO guys - superior from the POV of the multi-paradigm guys.

And yes, Java is far from being pure OO. Look at the primitive/reference/autoboxing mess. Or methods not being objects and having to write 90% anonymous inner class boiler plate code for 10% simple callback code.

Share this post


Link to post
Share on other sites
Thanks for the help so far.

Quote:
Original post by Ravyne
Most "pure" OOP languages don't have to make up for the shortcomings in C++ that are at the root of this idiom, most, simply put, have a better way built into the language. In other words, what is often a best practice for OOP in C++, doesn't even apply to other languages. Its a case of apples to oranges.


This is one of the main reasons I asked the question. What are the shortcomings specific to 'hybrid' languages (C++, PHP, whatever) that don't exist in 'pure' OO languages? What are these languages' "better way" of handling things?

I was under the impression that the ideas presented in the article were applicable to OOD in general, though.

Quote:
Toohrvyk's code


I understand this first solution as it has been spelled out for me. It "feels" a little weird, but I guess I can't see any immediate problems with it.

Unfortunately I'm actually working in Actionscript 3 and, stupidly, the language has no support for any sort of pure virtual or abstract functions. Instead, I end up having to write those as concrete, overrideable methods that throw exceptions, and it would be swell if I could avoid that pain in the ass.

I'm REALLY unsure if I'm understanding the second, similar suggestion:
Quote:
Or the equivalent based on an interface (which delegates the appropriate functions to the appropriate objects). [and Telastyn's subsequent agreement]


Here's my potentially wildly wrong attempt at understanding with a less-stupid example than in my original post. Let's say there exists this idea of a 'square' which carries a side length and area. The two need to be in sync, of course. Squares offer the ability to be resized, and we want to implement a 'grow' function which uses the resize function to change the dimensions of the square relative to its current size:


using System;

// Interface.. not strictly necessary (?)
// I'm unsure of what was intended by 'interface' in the description
public interface SquareDelegatorInterface
{
void resize(float newSize);
float getSideLength();
float getArea();

void grow(float amount);
}


// The "delegator"
public class SquareDelegator : SquareDelegatorInterface
{

public SquareDelegator(float sideLength)
{
this.square = new ConcreteSquare(sideLength);
this.helpers = new SquareFunctions();
}

public void resize(float newSize)
{
this.square.resize(newSize);
}

public float getSideLength()
{
return this.square.getSideLength();
}

public float getArea()
{
return this.square.getArea();
}

public void grow(float amount)
{
this.helpers.grow(this.square, amount);
}

private ConcreteSquare square;
private SquareFunctions helpers;
}


// "Free"-like functions
public class SquareFunctions
{
// ...or should this still be static?
// I don't intend to carry state here
public void grow(ConcreteSquare s, float amount)
{
s.resize(s.getSideLength() + amount);
}
}


// The actual square class
public class ConcreteSquare // : AbstractSquare or whatever
{
public ConcreteSquare(float sideLength)
{
this.sideLength = sideLength;
this.calculateArea();
}

public void resize(float newSize)
{
if (newSize < 0)
throw new Exception("Not gonna happen");
this.sideLength = newSize;
this.calculateArea();
}

public float getSideLength()
{
return this.sideLength;
}

public float getArea()
{
return this.area;
}

private float sideLength;
private float area;

private void calculateArea()
{
this.area = this.sideLength * this.sideLength;
}
}


public class Program
{
static void Main(string[] args)
{
SquareDelegatorInterface o = new SquareDelegator(5);
Console.Out.WriteLine(o.getArea());

o.grow(-2);
Console.Out.WriteLine(o.getArea());

Console.In.ReadLine();

}
}



("free"-like function can take AbstractSquare if it exists, of course)

Is this at all correct? If so, what advantages does it offer over the way I originally tried to do it? Just increased "object-yness"? I guess you could potentially override delegators' methods, as well as the 'grow' method. If this IS all correct, is there any reason the technique couldn't/shouldn't be used in one of those "multi-paradigm" (I don't like saying this as C#/AS have some functional constructs, and so may be also considered multi-paradigm) languages?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!