• Forum
  • Lounge
  • Is there a language that does "downclass

 
Is there a language that does "downclassing"/instance morphing?

Pages: 123
Solved: Python allows it (though without restriction based on type): http://ideone.com/GFKYXq

Consider you have A, B extends A, C extends B. You have an object which you have determined or can guarantee is an instance of B only, not of any class that extends B. You need to permanently convert B to an instance of C to adjust its behavior while maintaining its current state. Normally you could call C's copy constructor with the B instance, and then replace the existing B instance with he new C instance in whatever collection or data set it is in. But the problem here is that other code may still references or access to the old B instance when it *should* be updated with access to the new C object instead. The C object is fully compatible with the B object in terms of its public interface, so why can't the B object be "downclassed" or morphed into a C object? The external references all still point to the same object which is now not only a B object but also a C object.

Is there a language that solves this problem without the use of a design pattern? Is there a good design pattern to use for this in other languages?


I have a use-case with a complex setup that actually does exist:
There is a mod for Minecraft called Buildcraft, which adds pipes that items can be transported through. There are different kinds of pipes with different behaviors. There are also addon mods for Buildcraft that add new kinds of pipes with different behavior. It would make sense to be able to take a pipe, keep its state, and downclass it into another kind of pipe that extends the behavior of the existing pipe without breaking and replacing the pipe.

I'm probably just crazy here, but I can't see another way to solve this...
Last edited on
I suppose you could by using pointers and placement new
closed account (o1vk4iN6)
Why not just do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

class Pipe;

class Behavior
{
public:
     virtual ~Behavior();

     virtual void Tick( Pipe* ) = 0;
};

class Pipe
{
public:
     void Tick(){ /* call all behaviors tick */ }

private:     
     Material contents;
     std::vector< Behavior* > behaviors;

}


If I am understanding you correctly say you want to add a thermostat to a pipe so you can tell the temperature of the contents. You can add this to an existing pipe that will then display the temperature.
@guestgulkan There is no way to know how much memory is required without knowing in advance everything about every addon mod that will ever exist.

@xerzi That's a nice design pattern-based solution. It's been presented to me before. Not a language feature, though.
Last edited on
closed account (o1vk4iN6)
Why does it need to be a language feature ?

Well I could see it working some what like a virtual class, that way the memory doesn't need to be linear but then in reality it is just a pointer so rather then doing someptr->contents you are doing this->contents. I think they are essentially the same, if there is someway for it to require linear memory, allocate it on the heap then try to extend it, if it can't move it, etc... Not sure how that would work without some kind of pointer though. I'll think about it more when I have time.
xerzi wrote:
Why does it need to be a language feature ?
Because I specifically asked for it in both the thread title and the OP...

I also don't see why this is so complicated for some high level language to implement; you make it sound like you're thinking in a low-level environment, in which this kind of programming would not be done in the first place.
I'm not completely sure what you mean by "downcast but not really". Do you mean something like this:

1
2
3
4
5
6
7
8
9
class A {
   int x, y;
//...
};

class B : public A {
public:
   int some_func_not_in_a() { return x+y; }
}


? If so, then I think the problem is in the design of your classes. Class A has *decided* to not implement "some_func_not_in_a" but you want to have one anyway. If you wanted that functionality for all A then it should've been in A to begin with.
I mean nothing of the sort. The more-derived class will have exactly the same public interface as its parent, the difference will be in the behavior, which will be changed slightly. It would solve more problems and make much more sense if you could call B's constructor and make the object be an instance of B as well, throwing it further down the inheritance tree, while preserving its state and identity. Existing pointers/references would point/refer t the same object because it is still the same object, now of a more-derived type.

A C++ish Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class A
{
    //meh
};

class B : public A
{
    //meh - m + bl
};

class C : public B
{
    //bleh - eh + ah
};

//...

void DealWithAnObject(A *a)
{
    B *b = dynamic_cast<B *>(a);
    if(b == 0) return; //not what we're here for
    if(dynamic_cast<C *>(b) == 0)
    { //we know it's a B instance but not a C instance
        b->C::C(/*constuctor args*/); //convert object into a C instance
    }
    //done, a now behaves like a C object, and dynamic_cast<C *>(a) works
}
Last edited on
Ok, if I understand correctly, you have an instance of A and want to convert it to B while keeping all pointers to the original A object valid?

You could have a wrapper class that everything points to, then internally that class can handle allocating a B object and copying everything over from the A object. I don't know of any languages that have such a feature built in though.
Like I said, there are plenty of design patterns, but you really can't enforce them properly when all three classes are created by different developers that don't coordinate these things. It would make sense to allow such a process as a language feature as it would not be breaking any rules.
But the problem with your expectation is that C could have members and stuff that just a B can't use. I suppose you could have tons of compiler/run-time checks to make sure that C only messed with B's things, but if that was the case then those methods and stuff could easily just have been part of B in the first place.
The only really non-intrusive thing I could think of for C++ would be to make a PipePipe type that implemented the Pipe interface, but got it's behavior from a contained, existing Pipe type. You could then change the behavior of the PipePipe by changing the contained pipe.

Of course, it wouldn't allow you to change the behavior of normal pipes.

PipePipe pipe.

For the nth time already, I'm not looking for a design pattern, nor a solution in C++. I am asking if there exists a language such that this is a language feature and thus redundant to solve with a design pattern.
Last edited on
What I'm trying to say is that if you had two such classes, then the second class would be useless since all of it's methods could have just been in the first class to begin with.
But the behavior is different and you may want to only have the behavior of the first class...
> Like I said, there are plenty of design patterns, but you really can't enforce them properly when all three classes are created by different developers that don't coordinate these things.
I don't see how that makes it any harder. A, B, C form the inheritance.
You use them with the strategy pattern in Foo.

> It would make sense to allow such a process as a language feature as it would not be breaking any rules.
It would mean to have yet another assignation operation.
So you could copy, reference, be the same, be the same but maintain a different name...
ne555 wrote:
I don't see how that makes it any harder. A, B, C form the inheritance.
You use them with the strategy pattern in Foo.
But when you need t convert B into C, it is more convenient to have a language feature than to coordinate with three separate developers, some of which may no longer work on the code or even go online.
ne555 wrote:
It would mean to have yet another assignation operation.
So you could copy, reference, be the same, be the same but maintain a different name...
???
But the behavior is different and you may want to only have the behavior of the first class...


How? You still have all the functions of B, you are just adding some extra ones. Just don't use the other ones.
How do you not add the other ones when you con't even have the source to them? Did you even read my use case?
For the nth time already, I'm not looking for a design pattern, nor a solution in C++. I am asking if there exists a language such that this is a language feature and thus redundant to solve with a design pattern.

As far as I can tell that's the first time.

Is there a language that solves this problem without the use of a design pattern? Is there a good design pattern to use for this in other languages?

After asking if there's a language that solves this problem without the use of a design pattern, then asking about design patterns in other languages, one might assume you meant languages other than the (possible) one in your first question. C++ would be one of those, and it certainly looks like you were asking for a design pattern.

Pages: 123