Showing posts with label best practices. Show all posts
Showing posts with label best practices. Show all posts

Friday, January 28, 2022

Check in, check out, Daniel-san

When you start to work on projects with more than one developer, you suddenly find yourself having to solve what sounds like a very simple problem:  Sharing code.  At its heart it IS simple, but the reality is that you have to take a disciplined approach.  Trying to do this without using a tool designed for the job is a likely path to madness, and it's a bit mad not to do so given that the tools you need are widely available at no cost.  Every professional shop, open source project and even many independent individuals makes use of some kind of version control system.

Version control at first feels like a burden.  If anything though, it is quite the opposite.  Knowing that your old code is out there, ready to be brought back into your project anytime you need it?  That's gold.  It frees you up to experiment, to explore, to go down paths that you really aren't sure lead anywhere.

So how does one get started?  Naturally, as with anything else, it begins with education and access.  In this case, you need to determine what you have available to you first.  If there's an existing system ready for you to use, you probably want to take advantage of that.  If you're a clean slate, you need to get something set up.  There are many services out there which provide version control, and it can even be free if you don't have a problem with other people possibly seeing your code.  It can also be free if you feel safe just running everything on your own computer or a server you control, although then you may need to install a service and keep it running.

Here are some of the more popular version control systems:

CVS - An old standby, it still works but frankly it's lacking a bit in features more modern systems have.
SVN - More modern and quite functional, I have worked (and continue to do so) in subversion shops for years.
Bitkeeper - This was paid software for years and I've never actually used it myself .  It basically came about as an answer to the difficulties Linus Torvalds was having with Linux development.
Git - A slightly more convoluted system than some, but clearly very powerful.  This ALSO came about due to Linux development, and apparently due to issues Linus was having with Bitkeeper.

There are others, but these are probably the main ones most of you will be looking at.

I personally use Git (hosted on a service) for code I share on this blog and for my own experimental work. It doesn't cost me anything, and it's nicely integrated with my IntelliJ IDE.   It also supports something called a 'gist', which is (as far as I know) a unique way to share a subset of a project in order to request assistance or provide examples.

The basic idea behind version control is the same, no matter what system you use.  Your make changes to software on your own computer and make sure that things work the way you want.  When you're happy with the code, you check it in to your version control repository.  If you are unhappy with the code, or have broken something to the point where fixing it is a major burden, you can just pull the last working copy back down and you're back to a known good starting point.

If multiple programmers are working on a project, things are much the same, except that you will pull the last working copy down a bit more often as you are getting all the changes that others have checked in as well.  Things are quite simple as long as two developers aren't working on the same exact files.  If they are working on the same files, some manual intervention is likely going to be needed to ensure that changes don't conflict.  That last process is called 'merging'.


Merging is a source of difficulty, or it can be, depending on your development practices.  I prefer to keep commit changes small and isolated whenever possible.  This keeps the differences (deltas) down to manageable levels, and if I've added two new source files rather than modified an existing one, we're not going to run into any problems.

Thursday, December 11, 2014

Good Practices: Coding to the Interface

You will sometimes hear someone recommend that you 'code to the interface'.  What the heck does that mean?

Well, I'm going to get deeper into just what interfaces provide for you, and why you should both create and use them in a later post, but a simple example can be shown with the very common Collections interface "List".

Using List is very common, although a lot of people will begin using ArrayList directly.  It's not that there's anything precisely wrong with doing this,  In fact, probably nine or more out of ten times the only thing it will cost you is a few extra keystrokes.  However, it serves as a good example in a section of code with which many of us get familiar early.

Suppose I wanted to have a list of active elements for a game.  I could write this code to have it available within my GameData class:

    private ArrayList<GameElement> gameElements = new ArrayList<GameElement>();

And then I could have a getter method:

    public Arraylist<GameElement> getGameElements() {
        return gameElements;
    }

That's simple enough, right?  So I go ahead and write a bunch of code within the rest of my game, happily accessing all the elements they need to function:

    public void addElementToGame(GameElement newElement) {
        ArrayList<GameElement> allElements = myGameData.getGameElements();
        if (false == allElements.contains(newElement) {
            allElements.add(newElement);
        }
    }
    ...
    public void walkAllElements(ElementChecker myChecker) {
        ArrayList<GameElement> allElements = myGameData.getGameElements(); 

        for(GameElement element : allElements ) {
            myChecker.checkElement(element);
        }
    }

etc. etc. etc.

It's not that there is anything wrong with this.  In fact, you're feeling pretty good, you even made a method that accepts a worker object as a parameter and invokes some code on that worker object for each item in the list.

Then the reports start coming in:  Sometimes the game gets very slow.  It's generally after someone has been playing for a long time.  Eventually, you pin it down to your element management code.  It's fine with a few elements but once you get to having hundreds, certain operations get bogged down.  At the same time, you realize that you'd love to prioritize these game elements by allowing inserts to happen at the start of the list for faster processing.  That's got you down all weekend as you try to figure out what to do.

The light at the end of the tunnel comes when you review the documentation for the Collections framework and discover LinkedList.  It might be a bit slower with smaller amounts of data, but it sounds like it will behave better for you with the large data sets you're processing and it offers the side benefit of being able to easily handle quick inserts at the start of the list.

That's when you suddenly get annoyed.  You make the change in GameData and from all the red markers that show up on your screen you realize that you've got seven hundred and fifty places in your code base where you have to do nothing more than change the word 'ArrayList' to the word 'LinkedList'.  This is all fixable, but it's going to take up a significant chunk of your time.  Most of the code does not even have to change in any other way, maybe you've got two or five places where you will need to explicitly refer to methods that are specific to LinkedList, bu the rest are just mechanical updates of the code.

This is where coding to the interface could have saved you some time and effort.  If, instead of referring to 'ArrayList' everywhere, you had just used 'List', all or at least most of your code would have otherwise remained exactly the same.

The declaration of gameElements could have been written this way:

    private List<GameElement> gameElements = new ArrayList<GameElement>();

Your walkAllElements could have looked like this instead:

    public void walkAllElements(ElementChecker myChecker) {
        List<GameElement> allElements = myGameData.getGameElements(); 

        for(GameElement element : allElements ) {
            myChecker.checkElement(element);
        }

Note that nothing has really changed here except for changing 'ArrayList' to 'List' in some places.  This is because 'List' is an interface, which is really a kind of contract.  It forces an object that implements it to support a set of defined methods, and both ArrayList and LinkedList do this.

    public class ArrayList implements List {

    public class LinkedList implements List {

Now this just scratches the surface of interface usage.  Later on we'll get into creating and using your own, and how that can save you even more time and energy, and bring your coding to a new level.  For now though, just be aware that classes can implement interfaces, and that whenever possible, it's better to write your code to care only about said interfaces, and not to give a hang about the specific object type it's dealing with.  Get used to it, let it become your natural way of operating.  It will serve you well later on.  Eventually you will be creating your own interfaces, which can serve as the control points for widely varying objects.  I can drive a car from any manufacturer, because they all offer me the same set of controls.  I do not need to be licensed to drive a Subaru, separately licensed to drive a Volvo, and have a learner's permit for a Ford.  Interfaces are like specifying 'gas pedal, brake pedal, steering wheel'.  Oh, and objects can support arbitrary numbers of interfaces, so in reality my car is defined something like this:

    public class S60 implements Driveable, GasPowered, Geartronic, PushbuttonStart, KeylessEntry {

There would no doubt be a lot more if I sat down to think about it all.  The point is, once I understand how to drive a car, I can drive pretty much any car, because they ALL implement Driveable.  If I go to my local Avis and rent something, I may not know about all the features, the navigation system may be a complete mystery to me, but man, I know how to work a steering wheel.