Sign in to follow this  
probing

Game programming without OOP

Recommended Posts

probing    122
I've coded a couple of small games and they've all been written in object-oriented languages. I've found OO to be invaluable in my games programming experience. This is why I simply don't understand how a large game can be built without using classes and objects. Yet, most games are/were written in C. So, how is game code structured in non-OO languages? Say for example that we a class "Monster" with the property "hitpoints" and a method called "attack". Without the use of classes, how would we go about associating a thing that attacks and its hitpoints? I already have a few convoluted ideas about how I could go about this, but I'm looking for a way that scales well and isn't a complete pain to program. Thank you for the help. [Edited by - probing on March 13, 2010 7:40:22 PM]

Share this post


Link to post
Share on other sites
nullsquared    126
Quote:
Original post by probing
Yet, most games are/were written in C. So, how is game code structured in non-OO languages?


Object oriented programming is not bound to a language; it's completely possible to write OO code in C, it just wouldn't look like the C++ equivalent (no classes, etc.)

For example:

typedef struct player
{
vec2 position;
} player;

player *createPlayer()
{
player *p = (player*)malloc(sizeof(player));
p.position.x = 0;
p.position.y = 0;
return p;
}

void destroyPlayer(player *p)
{
free(p);
}

void movePlayer(player *p, const vec2 *m)
{
p->position = addVec2(&p->position, m);
}

// ...

player *p = createPlayer();

vec2 m = { 10, 0 }; // move 10 units right
movePlayer(p, &m);

// ...
destroyPlayer(p);

Share this post


Link to post
Share on other sites
probing    122
Yes, but my question is how would you go about coding a game without using OOP. Actually, the intent of my question is to find out how, when using a language that doesn't have strong support for OOP, what is the best way to code a game?

Share this post


Link to post
Share on other sites
apatriarca    2365
The code structure depends on the game (a 2D puzzle game and a 3D FPS aren't very similar for example) and on the language (C is completely different from haskell for example). It is impossible to give a satisfactory answer to a question like that. If you make your question a little more specific (a particular game in a particular language), we will be able to give a more specific and useful answer.

Share this post


Link to post
Share on other sites
probing    122
To be more specific, suppose we want to create a top-down 2D shooter in C. Is there a good method to follow, one that isn't OO, as nullsquared's is.

Share this post


Link to post
Share on other sites
Antheus    2409
The anti-OO argument is primarily about runaway hierarchies. Inheritance trees 10 levels deep, with hundreds of individual classes. Inevitably, the top-down inheritance will become problematic.

Counter-argument is about a different approach (it can be likened to ORM table design) towards structuring data.

Rather than expressing functionality via "X can do something", it is about saying "X has following things".

On one end, there is the entity approach. There is one single object type, which then contains functionality as members. Renderer, Input, Physics, ....

The simplest OO equivalent would be this:
class Object {
void jump();
void run();
void attack();
void die();
void foo();
...
};

To create a new object type, extend this and implement just what you need - leave the rest empty. If you add new functionality - add it to base class for *all* objects to have. Those that don't need it, won't implement it.
Since this doesn't allow code reuse between similar classes, such shared functionality would be implemented externally.

The benefit of this change is that inheritance is not forced by language and can be changed arbitrarily as needed without changing class designs. Zombie is no longer a Monster, but needs to use MonsterAIHandler? No problem.


At the far end of this spectrum, only archetypes are stored. Monster makes sense, Weapon and Armor might as well. The rest is done by configuration:
struct Monster {
int hitpoints;
int damage;
int speed;
bool undead; // zombies
bool holy; // paladins
};

Imagine Paladin and Zombie inheriting from same base class. You could not make Zombie Paladin.

The processing then also changes into something like this:
void updateMonsters() {
for (Monster m : monsters) {
if (m.undead) testForHolyDamage();
if (m.holy) applyHolyAura();
}
}

If you have separate Paladin and Zombie classes, interaction between holy and undead would need to be explicitly modeled, perhaps via helper classes, or extra damage, etc... Here you get all of this for free. This can become a problem as requirements change, and original class design is no longer suitable.


Two important things:
- Non-OOP designs will be implemented in OO language using objects. Unless people really enjoy pain
- Both, OOP and non-OOP designs can be badly messed up. Just in very different ways. OOP designs become rigid and difficult to extend, non-OOP designs become messy and can suffer from spaghetti code


Experience is about finding a proper compromise on which parts to apply rigid OO principles (emphasizing self-containment), and which to leave flatter and less structured.

There is no 10 steps to good design tutorial.

Share this post


Link to post
Share on other sites
Antheus    2409
There's another point to be made of what non-OOP allows. In an event-based system, things would look like this:

Monster::takeDamage(int d) {
hitpoints -= d;
onHealthChanged(this, d);
if (d < 0) onDie(this);
}

The above on___ are events. Whenever and really whenever health changes, they would be triggered. This is convenient, onhealthChange would display that -23 floating text. But at the same time, this can be either slow (imagine firing one such even each time a particle changes).

And here a design problem arises. Ideally, one would want to monitor each and every property change. Perhaps each particle (tens of millions of events firing each second). And when designing classes, each variable would need to predict which events it fires. Only health lost? Also gained? Are the same event? Different? Different events for different values?

This results in combinatorial explosion.

The "non-OO" design would be different:
void updateMonsters() {
for (Monster m : monsters) {
if (m.damageTaken > 0) {
m.hitpoints -= m.damageTaken;
m.damageTaken = 0; // reset damage
}
}
for (Monster m : monsters) {
if (m.oldHitpoints != m.hitPoints) {
displayFloatingText(m.oldHitpoints - m.hitPoints);
m.oldHitpoints = m.hitPoints;
}
}
}


Here hitpoints might change many times, but would trigger only one event. And if we want a different kind of event - we don't need to change classes, add more events, or anything - just add more functionality to post-processing step. Want to display only negative health (if (m.OldHitpoints > m.hitPoints)), only health gains, displayed in blue to show healing (if (m.oldHitpoints < m.hitPoints). Detect death (if m.hitPoints < 0) removeMonster(m)).

All this inside one single method, without changing the rest of classes, adding extra delegates, events, methods, handlers, callbacks, observers, .......


But this also exposes the downside, since the changes to data are no longer encapsulated, so two parts of code might be modifying same variables. Here is where OO comes in.

OO at higher level encapsulates these parts. There might be Monsters objects which is responsible for managing monsters, but cannot modify player. Or similar. And this is where the design must be thought over. Who knows about what, who can modify what, and this is where the OO principles pay off.


Which methodology is better depends. If using scripting, the non-OO approach might be slightly better. Most scripting languages do not have strong OO foundation, so they don't except strong OO concepts. No point in forcing it upon them, just give them a well defined interface.

Not separating core chunks into some form of OO would be a mistake and fallback to the almighty global state. It also isn't needed, it is fairly well understood which systems need to talk with each other.

Final detail - above approach can be considerably more performant than fully encapsulated OO. This may not matter much as far as PCs go, but streaming lots of data is friendlier to GPU, it is perhaps slightly easier for interaction with scripting languages, and is likely to incur much less overhead when running in sandboxes (Flash or similar). Finally, if using managed language, it requires much less memory allocations.

Overall, designing a non-OOP system today would be foolish, as would exposing global state (inhibits unit testing, runaway coupling). Polymorphism, inheritance and encapsulation are a good thing if applied at proper level. Central idea is to undertand what needs to be a self-contained, self-aware object, and what can be left as plain data and plain code.

Quote:
Here are a couple links that might interest you


While PS3 is apparently very OOP-hostile my objections are only marginally related to performance. I retain a more pragmatic approach dealing more with finding a proper level of abstraction and raising it above typical OO, for the sake of less formal and perhaps more flexible design, rather than strictly following everything-is-object mentality.

While at some point compile-time checking and rigid interfaces were considered superior, the explosion of dynamic languages has shown they have considerably less impact on code quality, and might actually be deterring it. So with all the automated testing possible today, it might be worth reconsidering some of the strict positions on just how much safety and encapsulation is needed in practice.

Share this post


Link to post
Share on other sites
kindjie    290
What you are asking about are the different programming paradigms. They are essentially different ways of thinking about programming. The following are some of the major ones:
- Object-Oriented, e.g. Java, Smalltalk, C++
- Procedural, e.g. Delphi, C, C++
- Functional, e.g. Lisp, Haskell
- Logic, e.g. Prolog

To go over how to write scalable applications in each would take a long time. Languages often do not lock themselves into one type - C++ can be written in an object-oriented or procedural way, for instance.

Briefly: Object-oriented applications encapsulate data and methods in objects - the data structures and subroutines are put together in an object.

Procedural applications are divided up into modules. The subroutines act on the data structures (see nullsquared's example which is actually procedural).

Note: The following I haven't written large-scale software in.
Functional applications have subroutines with no "side-effects" - they only look at their parameters and return values. They are not evaluated step by step, but consist of nested functions which can often be evaluated in any order. They are structured in modules, similar to a procedural applications.

Logic applications consist of a declared set of premises. Programs are structured similar to a mathematical proof, and you run things by trying out theorems.

A quick Google should give you more information on any of these.

Share this post


Link to post
Share on other sites
MarkS    180
It's funny, but I've asked the same question, but from the other end. I've never written an OOP program (game or otherwise) in my life and I cannot imagine any reason why I should start now.

OOP and procedural programming is almost identical from a code perspective. When I write a program, I use structs instead of classes, and I separate the code into files based on what it does. For instance, vertex.h would include the vertex data structure, any global variables used by the vertex function and function prototypes. The vertex.cpp (".c" seems to no longer be considered a valid file type) file contains the actual code for manipulating vertices. With C++, typically this would be coded as a single header file, with the vertex class containing both the variables in the C data structure, along with the global variables and actual functions with code instead of prototypes. If there is a .cpp file, it would typically be empty or have empty functions.

Ultimately, the same thing is accomplished. There are pros and cons to each style of programming, but you can do exactly the same with each one. It all boils down to what you are comfortable with.

Share this post


Link to post
Share on other sites
Matt_D    247
Quote:
Original post by maspeir
With C++, typically this would be coded as a single header file, with the vertex class containing both the variables in the C data structure, along with the global variables and actual functions with code instead of prototypes. If there is a .cpp file, it would typically be empty or have empty functions.


your lack of data hiding is concerning.
If your directly accessing struct members, or global variables, your codebase will be exceptionally brittle, this is not good. use accessors, even in structs. use data hiding, use abstract data types.

why? well, next time you want to swap out the array, for a linked list in one of your exposed struct's your going to have to do a hell of a lot of work to fix up everywhere your poking the internals. use accessors, hide your internals. itll make you far more productive.

also, dont put all your code in the header file, as this will be included in every file which includes the header, increasing your compilation time. and possibly exe size. use inline

and if you really are going to stick with C, for gods sake, use and understand the restrict keyword

as for C itself

- you end up using function pointers, or switch statements to do exactly what you get with type polymorphism anyway

so, your left with either a possible cache miss on a double indirection (vtable lookup) when using c++, or your left with either an instruction cache miss (function pointer) or instruction cache thrash (large switch statement)

either way, you get screwed, pick your poison :)

the ps3 isnt hostile to oop, it, along with modern architectures where memory latency is much greater than cpu cycle latency, are very sensitive to cache misses, thats all cache, including instruction cache. If you miss cache, your going to spend a lot of time waiting for your data to come back from main memory. but this isnt a reason to avoid c++, its a reason to be careful what you do, and when. If you expect to run a piece of code 100,000 times a frame, you want to avoid any sort of uncached memory lookup at all, including instruction cache.

if your doing it 3 times a frame, no one will care either way.

- you use abstract data types, and other data hiding mechanisms instead of using class based data hiding.

this is more of a style thing, its sometimes nice to be able to encapsulate data and functionality into a single object, sometimes its nice to have functionality separate from any type of data. both approaches are useful, and have their place within a game/engine/other.

when you need to do bulk processing, in a typical SoA layout, you want to have your processing in a flatter, ADT style.

if your doing things on individual objects, in a typical AoS layout, its far easier to use C++ and leverage the inherent polymorphism.

- people are scared of templates.

no really, they are. they are like all things, a tool, use them where they make sense, dont use them to fix every problem.
and in 13 years of professional life, ive never, ever, seen template metaprogramming in use outside of boost or stl.


basically, right now, leverage every language feature you can. you can always rip it out and replace it later if its slow. make it work first, then profile, then fix what's slow.

what's *actually* slow might surprise you.

and it most likely *wont* be vtable lookups, unless your doing something very wrong.

Share this post


Link to post
Share on other sites
MarkS    180
Quote:
Original post by Matt_D
Quote:
Original post by maspeir
With C++, typically this would be coded as a single header file, with the vertex class containing both the variables in the C data structure, along with the global variables and actual functions with code instead of prototypes. If there is a .cpp file, it would typically be empty or have empty functions.


your lack of data hiding is concerning.
If your directly accessing struct members, or global variables, your codebase will be exceptionally brittle, this is not good. use accessors, even in structs. use data hiding, use abstract data types.

why? well, next time you want to swap out the array, for a linked list in one of your exposed struct's your going to have to do a hell of a lot of work to fix up everywhere your poking the internals. use accessors, hide your internals. itll make you far more productive.

also, dont put all your code in the header file, as this will be included in every file which includes the header, increasing your compilation time. and possibly exe size. use inline

and if you really are going to stick with C, for gods sake, use and understand the restrict keyword


Again, I've never written an OOP app in my life. I HAVE looked at plenty of OOP code (mostly C++) and I do understand it. What I wrote was based on what I have seen in some of the C++-based game engines I've looked at. I'm not saying it is good programming practice, but it seems to be common. I abhor code in header files!

Share this post


Link to post
Share on other sites
flodihn    281
Antheus
Quote:

Overall, designing a non-OOP system today would be foolish, as would exposing global state (inhibits unit testing, runaway coupling). Polymorphism, inheritance and encapsulation are a good thing if applied at proper level. Central idea is to undertand what needs to be a self-contained, self-aware object, and what can be left as plain data and plain code.


It seems that you don't have much experience in non-OOP programming.
I am implementing my servers in Erlang which is a functional language.

I don't have any global state (the language doesn't even support global variables).

I don't do any unit testing yet, but when it comes to that I will probably use eunit, which comes bundled with Erlang.

Polymorphism is actually more easy to accomplish than in most OO languages (in my experiences). Here is a simplified code example how polymorphism can be achieved:


start() ->
State = #obj{module=my_first_module},
Pid = spawn(loop, State),
Pid.

% Here we loop for ever, we listen to two types of messages, one that tells us
% to tranform to another module and one that calls the function in our current
% module.
loop(#obj{module=Module} = State) ->
receive
{transform, NewModule} ->
NewState = State#obj{module=NewModule},
loop(NewState);
{call, Fun, Args} ->
Module:Fun(Args),
loop(State)
end.



Then from some other part in my code I can do something like this:

Pid = start(),
% This sends the message that tells the process to change module. All future
% calls will now be made in my_second_module instead of my_first_module.
Pid ! {transform, my_second_module}


Ineritance, I do this my keeping a list of modules which I iterate through to find the function to be called, when found I cache the result. This is not as easy as OOP inheritance but still quite trival to implement.

Encapsulation of state is very common in Erlang. You build your programs by letting a large number of processes iterating over their own state, as the loop example above. Processes can not change each other states directly but rather send a messages to each other, asking for changes/updates.

Inheritance and polymorphism is not exclusive to OOP and OOP design is not always the best way to structure a system, beliving so is quite ignorant.

[Edited by - flodihn on March 14, 2010 6:29:30 AM]

Share this post


Link to post
Share on other sites
kindjie    290
Quote:
Original post by flodihn
Antheus
Quote:

Overall, designing a non-OOP system today would be foolish, as would exposing global state (inhibits unit testing, runaway coupling). Polymorphism, inheritance and encapsulation are a good thing if applied at proper level. Central idea is to undertand what needs to be a self-contained, self-aware object, and what can be left as plain data and plain code.


Inheritance and polymorphism is not exclusive to OOP, and OOP design is not always the best way to structure a system, beliving so is quite ignorant.


I have to agree with flodihn. Most of the issues you raised were addressed with structured programming years before object-oriented programming became popular. Many other programming paradigms (especially functional) actually have much more powerful concepts of polymorphism. The main driver for object-oriented programming was code reuse (based on my understanding of interviews with Alan Kay).

That being said, sticking to the discipline of structured programming and object-oriented principles usually results in stable, maintainable software. Just keep in mind that other paradigms can get you there as well.

Share this post


Link to post
Share on other sites
rubicondev    296
I am familiar with modern style C++, but I don't like it tbh especially for games.

I prefer "C with Classes" and don't overuse most of the higher concepts. One level of inheritance is what I try to stick to for example. Maybe more if it's a straight line - a better description would be only one level of inheritance per class declaration :)

I've found it gives me the best of both worlds without any of the drawbacks, but I guess mileage is going to vary between different programmers and even the same programmer on different types of project.

I go way back and have worked on a quite a few larger games using just functional C and even assembler. I can say that in all honesty when I first read up on C++, it felt like a breath of fresh air and things definitely got easier to design. We'd done OOP concepts in functional C, but with little to enforce it, bugs were rife. If nothing else, formal classes impose enough rules to stop you making silly mistakes and to make you think a bit before doing a big one! :)

Share this post


Link to post
Share on other sites
Antheus    2409
Quote:
Original post by flodihn
Antheus
Quote:

Overall, designing a non-OOP system today would be foolish, as would exposing global state (inhibits unit testing, runaway coupling). Polymorphism, inheritance and encapsulation are a good thing if applied at proper level. Central idea is to undertand what needs to be a self-contained, self-aware object, and what can be left as plain data and plain code.


It seems that you don't have much experience in non-OOP programming.


Thank you for this useful insight.

It's always nice to hear opinions from a college student reporting on hobby project experience.

Quote:
I am implementing my servers in Erlang which is a functional language.

I suggest you read the posts I've made above carefully again. The advice I give was the following:
- Seeking a silver bullet, or sticking to one single methodology is not worth it
- Higher level design is usually easiest to do with OOP
- Lower level design should be non-OOP

To not make any claims on my own, I will quote the following:
Quote:
The sequential subset of Erlang is a functional language
Quote:
For concurrency it follows the Actor model


What surprise. Higher level design is OOP (actor model is the canonical definition of OO), and the lower levels are non-OOP.

Quote:
Inheritance and polymorphism is not exclusive to OOP and OOP design is not always the best way to structure a system, beliving so is quite ignorant.


Let's try to steer away from words like 'ignorant'. Dunning-Kruger and all that.

Share this post


Link to post
Share on other sites
kindjie    290
Quote:
Original post by Antheus
What surprise. Higher level design is OOP (actor model is the canonical definition of OO), and the lower levels are non-OOP.


I know I'm risking sounding pedantic, but actor model and object-oriented programming mean different things and arose to solve different problems. Actor model is a way of dealing with concurrency, whereas object-oriented programming is primarily focused on imperative programming and dealing with code reuse.

Share this post


Link to post
Share on other sites
the456    104
Here is a link where some is using Clojure and Functional Programming concepts to create a simple RPG.

http://briancarper.net/blog/making-an-rpg-in-clojure-part-one-of-many

Michael

Share this post


Link to post
Share on other sites
Beyond_Repair    116
I've written some simple games in C and quite frankly I ended up emulating OOP to the point C++ would simply have been a much better choice. Well written C ends up being fairly verbose after all; saving typing via use of global variables is a bad idea. Thus you'll get structs and functions operating on structs that's not being very different than C++ with more verbose syntax.

If you're trying to model something that can be naturally thought of to consist of objects, OOP is a very good approach. (How surprising!)

Now, on the other hand I gained very little from trying to write a compiler in Java compared to C (if we exclude things such as memory management). That's because writing a compiler is mainly about the algorithms used, and much less about the data structures, that are often quite trivial. Shoehorning algorithms into functor objects can be done but it doesn't add much from a syntactic perspective. Operator overloading is nice for stuff like vectors, but not necessary.

Overall C has a reasonable syntax for imperative algorithm implementation. The ugly stuff is mainly related to pointer manipulation.

And to summarize after this derailment: For games I see not reason not to use OOP fgiven how easily games can be modeled by classes. I make reservations for functional programming however that I'm not skilled enough to comment on.

Share this post


Link to post
Share on other sites
flodihn    281
Quote:
Original post by Beyond_Repair
I've written some simple games in C and quite frankly I ended up emulating OOP to the point C++ would simply have been a much better choice. Well written C ends up being fairly verbose after all; saving typing via use of global variables is a bad idea. Thus you'll get structs and functions operating on structs that's not being very different than C++ with more verbose syntax.

If you're trying to model something that can be naturally thought of to consist of objects, OOP is a very good approach. (How surprising!)

Now, on the other hand I gained very little from trying to write a compiler in Java compared to C (if we exclude things such as memory management). That's because writing a compiler is mainly about the algorithms used, and much less about the data structures, that are often quite trivial. Shoehorning algorithms into functor objects can be done but it doesn't add much from a syntactic perspective. Operator overloading is nice for stuff like vectors, but not necessary.

Overall C has a reasonable syntax for imperative algorithm implementation. The ugly stuff is mainly related to pointer manipulation.

And to summarize after this derailment: For games I see not reason not to use OOP fgiven how easily games can be modeled by classes. I make reservations for functional programming however that I'm not skilled enough to comment on.


Yes, I agree that OOP (and imperative programming) is very convenient when you are modelling a system, in theory.
However when it comes to simulating real world this model falls flat because the lack on concurrency.

I am not saying OOP and imperative models are useless but for modeling system which need to simulate real world properties, the language must support concurrency.

Hopefully in the future, there will be at least one language that takes the best of two worlds, imagine Erlangs concurrency, distribution, fault tolerance and OTP libraries + and Pythons easy syntax, general libraries and object orientation (no python-stackless is not even near).

Share this post


Link to post
Share on other sites
flodihn    281
Quote:
Original post by Antheus
What surprise. Higher level design is OOP (actor model is the canonical definition of OO), and the lower levels are non-OOP.


So you claim that higher level design is OOP and since the actor model is OOP, Erlang is OOP?
First of, Erlang begun development in the 80's. That is 10 years before GOF and design patterns become popular.
I am fairly sure that the Erlang team did not have access to a time machine.
Here is a link to Ericssons flagship ad301 ATM switch, is has 1 millions lines of Erlang code, 400 000 lines of C code and 13 000 lines of Java. Do you think they made the system that large without proper design and do you really belive the design is OOP even when using implementation languages that is non-OOP.
Here is a link:
http://www.erlang.se/publications/Ulf_Wiger.pdf

And by what means do you make the connection between the actor model and OOP, other than that GOF was heavily oriented towards C++ implementations.

Actor model is just a common name for a general solution to a problem, it is not something exclusive to OOP.

Share this post


Link to post
Share on other sites
Dragon88    246
I want to reiterate what the 2nd post of this thread said. You can write OO in C. Just because something doesn't use "classes" and "inheritance" and the like doesn't mean it's not "OO" in some way. It's just a somewhat different architecture of the same thing, for the most part.

I speak as a longtime (the last 10 years, up to the present day) C programmer. structs are my objects, and I have to pass "this" manually, and a few of my functions have global side-effects that wouldn't be there in a good design. Nonetheless, I would consider my code structure to be mostly "Object Oriented."

Frankly I can't imagine how you would go about making a serious game that WASN'T "object oriented" to a large degree. Note that when I say this, I am not refering to proper "OOP" practices, but instead to the OP's statement:
Quote:
This is why I simply don't understand how a large game can be built without using classes and objects.


The short of it is that I don't think you can (and you sure as hell shouldn't try). Pretty much everything you see when you play a game is an "object" and to model it as something else is just stupid. The specifics of how you model them may vary, but ultimately you end up with "objects" with their private data, and their manipulators.

The differentiating factor, then, is what tools you give yourself as a programmer. You can work in C and deprive yourself of proper inheritance and all the like[*], or you can work in C# and have a very rich "OO" toolset. I enjoy both, and for different reasons.

[*] Sure, you could reimplement them if you really wanted to, but imo you'd need a pretty damn good reason not to just use a different language.

Share this post


Link to post
Share on other sites
Antheus    2409
Quote:
Original post by flodihn
First of, Erlang begun development in the 80's. That is 10 years before GOF and design patterns become popular.


Object oriented (programming or design) has nothing to do with GoF or design patterns. Nor are patterns of design exclusive to OO languages.

Share this post


Link to post
Share on other sites
Nypyren    12061
Quote:
Original post by Beyond_Repair
I've written some simple games in C and quite frankly I ended up emulating OOP to the point C++ would simply have been a much better choice.


I'm running into the exact same problem trying to learn F#. I keep writing code as if I were using C#, which misses the whole point - trying to write OOP imperative code in F# is counterproductive. I just need to get used to F# before I can really start using it how it's meant to be used.

Perhaps the same issue I have carries over for other people as well? Using a specific development technique and then trying a language that doesn't REALLY work the same way... and you'll try to develop the same way as you're used to?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this