Graham Lee - Big Nerd Ranch Tue, 19 Oct 2021 17:46:20 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 Dependency Injection, iOS and You https://bignerdranch.com/blog/dependency-injection-ios-and-you/ https://bignerdranch.com/blog/dependency-injection-ios-and-you/#respond Sat, 12 Apr 2014 02:47:23 +0000 https://nerdranchighq.wpengine.com/blog/dependency-injection-ios-and-you/ Dependency injection refers to the design principle of telling a class which other objects its instances should work with, improving the flexibility with which the class can be used in different contexts. There are no special tools or libraries needed, and it's easy to adopt dependency injection in legacy code to enable testability.

The post Dependency Injection, iOS and You appeared first on Big Nerd Ranch.

]]>
Object-oriented programming is all about describing your software problem in terms of a network of objects and the messages they send to each other. In an iOS app, this network starts with the UIApplication instance, which sets up a load of framework objects before telling your app delegate that it has finished launching.
The app delegate is the seed from which you build the object graph that supplies your app’s features: the models, views and controllers all have to start somewhere, and this is the place. But it’s important that this graph not be brittle. If the objects in your app make too many expectations on what’s going on around them, it can be hard to adapt them to new requirements, make bug fixes, add new features, and test components in isolation.

What’s a dependency injection and why should I buy one?

The reason brittle object graphs are bad is that you cannot easily replace parts of the application. If an object expects to ask its environment for a load of other objects around it, then you cannot simply tell it that it should be using another object. Dependency injection fixes that. It tells the object, “Hey, these are the objects you should work with,” so that if we want to change the collaborators we just inject different things.
Why is that useful? In a previous post, I described how mock objects can be used as stand-ins for production objects, to aid in designing their interaction and to remove complicated behaviour in the tests. That’s one reason to want to swap collaborators.
Another benefit is that algorithms can be reused in multiple contexts, receiving data from different sources and delivering results to different destinations. To illustrate this situation, consider the difference between an iPod and a music box.
Music box “Music Box in the grass-1” by Flickr user zeevveez
The tune that a music box can play is encoded in the pattern of notches on the drum. If you want to play a different tune, you have to take the whole device apart, replace the drum, and put it back together. Conversely, an iPod has a USB interface for giving it different tunes. It’s just a player of noise, and the specific noise is easy to change.
Similarly, the music box only has one output mechanism: the metal prongs that are picked by the prongs on the drum (musical instrument buffs will recognise that this makes the music box a kind of lamellophone). As with changing the tune, changing how it plays that tune involves disassembling the box and replacing the prongs. Meanwhile, the iPod just has an output socket. Anything that has a plug compatible with that socket and the signals produced can play the music being generated by the iPod. If you’re bored of headphones and want to play your Herb Alpert tunes over the public address system at the Rose Bowl, you simply unplug one and plug in another, almost literally injecting that dependency into the iPod.
So, back to programming. Dependency injection is all about increasing the flexibility and adaptability of code modules such as classes. What does that look like in Objective-C?

Worked example

Imagine the following class is part of an existing app to browse open-source code. Part of its feature set lets us see the home pages of a couple of code hosting websites.

    @interface BNRCodeHostFetcher : NSObject
    - (void)fetchGithubHome;
    - (void)fetchBitbucketHome;
    @end
    @implementation BNRCodeHostFetcher
    - (void)fetchGithubHome
    {
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"http://www.github.com"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
            NSDictionary *userInfo = @{ @"data": data,
                                        @"response": response,
                                        @"error": error };
            [[NSNotificationCenter defaultCenter] postNotificationName:@"BNRGithubFetchCompletedNotification" object:self userInfo:userInfo];
        }];
        [task resume];
    }
    - (void)fetchBitbucketHome
    {
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"http://www.bitbucket.org"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
            NSDictionary *userInfo = @{ @"data": data,
                                        @"response": response,
                                        @"error": error };
            [[NSNotificationCenter defaultCenter] postNotificationName:@"BNRBitbucketFetchCompletedNotification" object:self userInfo:userInfo];
        }];
        [task resume];
    }
    @end

As is often the way with legacy code, there’s a lot to dislike about the design of this class. There’s plenty of duplication and notifications are an “interesting” choice for communicating completion to the outside world, but most importantly for our purposes, it relies on grabbing shared objects from its environment: the URL session and the notification centre. If we want to test this class in isolation, we’ll need to be able to substitute mocks for these objects, and you can imagine wanting to change the URL session to get different session configurations, perhaps to change the caching behaviour.
These are, to use the nomenclature from Michael Feathers’ Working Effectively with Legacy Code, hidden dependencies. If you’re looking at the class interface, you cannot see anything to suggest that the class might depend on an NSURLSession or NSNotificationCenter, but there they are.

A bit of refactoring

The first change is to make these dependencies explicit (and slightly reduce the code duplication along the way) by extracting a getter for each of the shared objects. This doesn’t change the behaviour, but as the class isn’t yet under test, it’s best to take baby steps.

    @interface BNRCodeHostFetcher : NSObject
    @property (nonatomic, readonly) NSURLSession *session;
    @property (nonatomic, readonly) NSNotificationCenter *notificationCenter;
    - (void)fetchGithubHome;
    - (void)fetchBitbucketHome;
    @end
    @implementation BNRCodeHostFetcher
    - (NSURLSession *)session
    {
        return [NSURLSession sharedSession];
    }
    - (NSNotificationCenter *)notificationCenter
    {
        return [NSNotificationCenter defaultCenter];
    }
    - (void)fetchGithubHome
    {
        NSURLSession *session = [self session];
        NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"http://www.github.com"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
            NSDictionary *userInfo = @{ @"data": data,
                    @"response": response,
                    @"error": error };
            [[self notificationCenter] postNotificationName:@"BNRGithubFetchCompletedNotification" object:self userInfo:userInfo];
        }];
        [task resume];
    }
    - (void)fetchBitbucketHome
    {
        NSURLSession *session = [self session];
        NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"http://www.bitbucket.org"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
            NSDictionary *userInfo = @{ @"data": data,
                    @"response": response,
                    @"error": error };
            [[self notificationCenter] postNotificationName:@"BNRBitbucketFetchCompletedNotification" object:self userInfo:userInfo];
        }];
        [task resume];
    }
    @end

Now there’s a single place to go to affect the collaborating objects used by each of the fetch methods: If the accessor returns a different object, that will be used. To continue using Michael Feathers’ terminology, these accessors have introduced a seam where two parts of the app’s behaviour come together. In this case, the seam is between the use of framework objects, and the supply of framework objects.
The next task is to introduce an inflection point, a mechanism for taking advantage of the seam. Chained initialisers are one way to let users of this class inject those framework objects. Deleting the custom accessors we just introduced means that the class will use whatever was passed in its initialiser, instead of the objects it had discovered itself. It’s not the only way: Subclassing and overriding the accessors would also work. In this example, we’ll explore the approach using a custom initialiser.

    @interface BNRCodeHostFetcher : NSObject
    @property (nonatomic, readonly) NSURLSession *session;
    @property (nonatomic, readonly) NSNotificationCenter *notificationCenter;
    - (instancetype)initWithURLSession:(NSURLSession *)session notificationCenter:(NSNotificationCenter *)center;
    //...
    @end
    @interface BNRCodeHostFetcher ()
    @property (nonatomic, strong, readwrite) NSURLSession *session;
    @property (nonatomic, strong, readwrite) NSNotificationCenter *notificationCenter;
    @end
    @implementation BNRCodeHostFetcher
    - (instancetype)initWithURLSession: (NSURLSession *)session notificationCenter: (NSNotificationCenter *)center
    {
        self = [super init];
        if (self)
        {
            self.session = session;
            self.notificationCenter = center;
        }
        return self;
    }
    - (instancetype)init
    {
        return [self initWithURLSession:[NSURLSession sharedSession]
                    notificationCenter:[NSNotificationCenter defaultCenter]];
    }
    //...
    @end

What did that get us?

There’s no change to the behaviour in the app: it still sets up its code host fetcher with -init, which means it uses the default NSNotificationCenter and NSURLSession. But now this class can be tested. You could use OCMock or a similar tool to inspect the collaboration between the code host fetcher and its collaborators. Once you can test a class in isolation, validating and correcting its behaviour becomes much easier.

That’s it?

Yes, there’s nothing up my sleeve; that’s really all there is to dependency injection. As James Shore put it:

“Dependency Injection” is a 25-dollar term for a 5-cent concept.

There’s no need for drastic rework to introduce new frameworks or libraries, though those libraries do exist. All you really need to do to get started is to look at an object, work out what else it needs to talk to, and then decide how you can get it talking to something else instead.

The post Dependency Injection, iOS and You appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/dependency-injection-ios-and-you/feed/ 0
Making a Mockery with Mock Objects https://bignerdranch.com/blog/making-a-mockery-with-mock-objects/ https://bignerdranch.com/blog/making-a-mockery-with-mock-objects/#respond Tue, 11 Mar 2014 23:10:29 +0000 https://nerdranchighq.wpengine.com/blog/making-a-mockery-with-mock-objects/ Mock objects are used by many developers when they're using test-driven development to design their systems, but what is it? And what are all these subtypes like partial mocks and nice mocks? Are mock objects usually nasty but impartial? Let's take a look, using examples from the OCMock framework for Objective-C testing.

The post Making a Mockery with Mock Objects appeared first on Big Nerd Ranch.

]]>

Mock objects are used by many developers when they’re using test-driven development to design their systems, but what is it? And what are all these subtypes like partial mocks and nice mocks? Are mock objects usually nasty but impartial? Let’s take a look, using examples from the OCMock framework for Objective-C testing.

Designing with mock Objects

There are two different problems we can solve with mock objects. The first (and the one they’re designed for) arises when we’re using test-driven development to drive the design of a class. Imagine the scene: you’ve built your first test, which tells you something about the API for your first class. Your test calls a method on your new class, and you know that in response, the object should grab some information from one of its collaborators. The issue is that this collaborator doesn’t exist yet, and you don’t want to put aside the test you’re already writing to start designing out that class instead.

You can create a mock object to stand in for this nascent collaborator. The mock lets you express the expectation that the object you’re testing calls through to the collaborator, and, if you need it to, it can also return a value that your test controls. Your test can verify that the methods you expect are actually called, with the test failing if that doesn’t happen.

In this scenario, the mock object is acting like a VCR, but without the chunky 1980s styling and the mangled ribbons of tape. During your test, the mock records every message you sent to it. Then you ask it to play that back, and compare it with the list of messages you wanted to send. Just like with a VCR, if you were expecting Gremlins 2 but actually recorded the news and the first half of an episode of Cheers, that’s a disappointing failure.

The key part is that you don’t actually need to build the collaborating object. In fact, you don’t need to worry at all yet about how it will be implemented. All that matters is that you express the messages it responds to, so that the mock object can test whether they’re sent. In effect, the mock lets you say, “I know that at some point I’ll want this, but I don’t want to be distracted by thinking about it.” It’s like a to-do list for TDDers.

simple-mock

Let’s look at an example. Imagine that Big Nerd Ranch has identified a gap in the market for museum inventory management apps. Museums have these huge collections of artefacts, and they need to be able to see what they’ve got, and organise it to work out what to put in their galleries, which might be laid out by theme, country, era and so on. A simple requirement for an app to look after this inventory might be:

As the curator, I want to see all of the artefacts in the collection so that I can tell a story to our visitors.

I’ll write a test that shows that the inventory can be asked for its list of all artefacts. There’ll be some other object that stores all the artefacts on disk, but I don’t want to worry yet how that works, so I’ll just create a mock for the store interface. My test looks like this:

    @implementation BNRMuseumInventoryTests
    - (void)testArtefactsAreRetrievedFromTheStore
    {
        //Assemble
        id store = [OCMockObject mockForProtocol:@protocol(BNRInventoryStore)];
        BNRMuseumInventory *inventory = [[BNRMuseumInventory alloc] initWithStore:store];
        NSArray *expectedArtefacts = @[@"An artefact"];
        [[[store expect] andReturn:expectedArtefacts] fetchAllArtefacts];
        //Act
        NSArray *allArtefacts = [inventory allArtefacts];
        //Assert
        XCTAssertEqualObjects(allArtefacts, expectedArtefacts);
        [store verify];
    }
    @end

To make that test compile, I have to create the BNRMuseumInventory class and its -initWithStore: and -allArtefacts methods.

    @interface BNRMuseumInventory : NSObject
    - (id)initWithStore:(id <BNRInventoryStore>)store;
    - (NSArray *)allArtefacts;
    @end
    @implementation BNRMuseumInventory
    - (id)initWithStore:(id <BNRInventoryStore>)store
    {
        return nil;
    }
    - (NSArray *)allArtefacts
    {
        return nil;
    }
    @end

I also have to define the BNRInventoryStore protocol and its -fetchAllArtefacts method, but I don’t need to implement them yet. Why did I define this as a protocol, and not as another class? For flexibility: I know what messages I want to send to a BNRInventoryStore, but I don’t yet need to worry about how it does them. Using a protocol here lets me be completely flexible about how the store is implemented, as it can be any type of class, as long as it responds to the messages I care about.

    @protocol BNRInventoryStore <NSObject>
    - (NSArray *)fetchAllArtefacts;
    @end

Now there’s enough information for the compiler to compile and run the test, but it doesn’t pass yet.

    Test Case '-[BNRMuseumInventoryTests testArtefactsAreRetrievedFromTheStore]' started.
    /Users/leeg/BNRMuseumInventory/BNRMuseumInventory Tests/BNRMuseumInventoryTests.m:91: error: -[BNRMuseumInventoryTests testArtefactsAreRetrievedFromTheStore] : ((allArtefacts) equal to (expectedArtefacts)) failed: ("(null)") is not equal to ("(
        "An artefact"
    )")
    <unknown>:0: error: -[BNRMuseumInventoryTests testArtefactsAreRetrievedFromTheStore] : OCMockObject[BNRInventoryStore]: expected method was not invoked: fetchAllArtefacts
    // snip more output

The assertion in the test has detected that the expected collection of artefacts was not returned, and the mock object has failed verification as the -fetchAllArtefacts method was not called. Fixing both of these problems gets us to a passing test.

    @implementation BNRMuseumInventory
    {
        id <BNRInventoryStore> _store;
    }
    - (id)initWithStore:(id <BNRInventoryStore>)store
    {
        self = [super init];
        if (self)
        {
            _store = store;
        }
        return self;
    }
    - (NSArray *)allArtefacts
    {
        return [_store fetchAllArtefacts];
    }
    @end

Mocks for integration

The other way in which we can use mock objects is to investigate integration with external code, such as Apple’s frameworks or third-party libraries. The mock object can remove all of the complexity associated with using the framework, so the test doesn’t need to create a full-blown environment just to ensure a small part of our app’s connection to that environment. This use of mock objects follows a test pattern called the Humble Object.

mock-as-integration

Extending the VCR analogy, we don’t get to design our interaction with a framework class, but we do want to check that we follow their rules correctly. If you’ve bought a VHS recorder, you don’t get to decide what type of tapes to push in. You have to use VHS tapes because the manufacturer made that decision for you. We can tell our mock object to expect a VHS tape, and to fail if we give it a Betamax tape.

Returning to our museum example, the first visible screen when the app is launched should be the list of all the museum’s artefacts. This can be arranged using UIKit by setting the window’s root view controller. Setting up the whole window environment for this test could be slow and complicated, so let’s just replace the window with a mock object.

    @implementation BNRAppDelegateTests
    - (void)testFirstScreenIsTheListOfAllArtefacts
    {
        BNRAppDelegate *appDelegate = [[BNRAppDelegate alloc] init];
        id window = [OCMockObject mockForClass&#58;[UIWindow class]];
        appDelegate.window = window;
        [[window expect] setRootViewController&#58;[OCMArg checkWithBlock&#58;^(id viewController) {
            return [viewController isKindOfClass&#58;[BNRAllArtefactsTableViewController class]];
        }]];
        [appDelegate application&#58;nil didFinishLaunchingWithOptions&#58;nil];
        [window verify];
    }
    @end

To make this test pass, implement the app delegate method.

    @implementation BNRAppDelegate
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options
    {
        self.window.rootViewController = [[BNRAllArtefactsTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
        return YES;
    }
    @end

Nice mocks

There’s another requirement we have to satisfy in launching a UIKit app, which is that the window containing the initial view controller’s view must be made key and visible. We can add a test that expresses that requirement. Notice that because both this test and the prior test use the same objects, the construction can be factored into a setup method.

    @implementation BNRAppDelegateTests
    {
        BNRAppDelegate *_appDelegate;
        id _window;
    }
    - (void)setUp
    {
        _appDelegate = [[BNRAppDelegate alloc] init];
        _window = [OCMockObject mockForClass:[UIWindow class]];
        appDelegate.window = _window;
    }
    - (void)testWindowIsMadeKeyAndVisible
    {
        [[_window expect] makeKeyAndVisible];
        [_appDelegate application:nil didFinishLaunchingWithOptions:nil];
        [_window verify];
    }
    - (void)testFirstScreenIsTheListOfArtefacts
    {
        [[_window expect] setRootViewController:[OCMArg checkWithBlock:^(id viewController) {
            return [viewController isKindOfClass:[BNRAllArtefactsTableViewController class]];
        }];
        [_appDelegate application:nil didFinishLaunchingWithOptions:nil];
        [_window verify];
    }
    @end

Now we have a difficult problem. The new test fails for two reasons: the expected -makeKeyAndVisible message is not being sent, and an unexpected message setRootViewController: is being sent. Adding the -makeKeyAndVisible message in -[BNRAppDelegate application:didFinishLaunchingWithOptions:] means that both tests fail, because now the mock window is receiving one unexpected method in each test.

Nice mocks fix that. A nice mock records the messages it receives, just like a regular mock, but it doesn’t worry about receiving messages that it wasn’t told to expect. It’s like saying, “I want to record that episode of Star Trek: Voyager, but I don’t mind if you caught the weather forecast before it.” It just ignores the extra messages, and doesn’t consider their presence as a need to fail the test.

We can change the test’s mock window to be a nice mock in -setUp.

    - (void)setUp
    {
        _appDelegate = [[BNRAppDelegate alloc] init];
        _window = [OCMockObject niceMockForClass:[UIWindow class]];
        appDelegate.window = _window;
    }

Now it’s possible to change the app delegate to make both tests pass.

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options
    {
        self.window.rootViewController = [[BNRAllArtefactsTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
        [self.window makeKeyAndVisible];
        return YES;
    }

Partial mocks

Sometimes you don’t need to replace all of an object’s behaviour with a mock. You just want to stub out a method to remove some dependency or complex behaviour, the result of which will be used in the method you do want to test. You could create a subclass and override the complex method with your stub, but it’s easier to use a partial mock. Partial mocks act as proxies to real objects, intercepting some messages but using the real implementation for messages they weren’t told to replace.

partial-mock

Back in our museum inventory app, curators need to filter the collection of artefacts by country of origin. That means looking at the list of all artefacts and applying some test to those objects, and we made the -allArtefacts method communicate with a store object. That’s not something we need to worry about in this test: we want to concentrate on the filtering, without repeating what we’ve already done in the collaboration with the store. Using a partial mock of the inventory object lets us stub out that part of the class. Writing this test also drives out some of the design of the artefact model.

    @implementation BNRMuseumInventoryTests
    {
        BNRMuseumInventory *_inventory; //created in -setUp
    }
    //...

    - (void)testArtefactsCanBeFilteredByCountryOfOrigin
    {
        id romanPot = [OCMockObject mockForProtocol:@protocol(BNRArtefact)];
        [[[romanPot stub] andReturn:@"Italy"] countryOfOrigin];
        id greekPot = [OCMockObject mockForProtocol:@protocol(BNRArtefact)];
        [[[greekPot stub] andReturn:@"Greece"] countryOfOrigin];
        id partialInventory = [OCMockObject partialMockForObject:_inventory];
        [[[partialInventory stub] andReturn:@[romanPot, greekPot]] allArtefacts];
        NSArray *greekArtefacts = [partialInventory artefactsFromCountry:@"Greece"];
        XCTAssertTrue([greekArtefacts containsObject:greekPot]);
        XCTAssertFalse([greekArtefacts containsObject:romanPot]);
    }
    @end

In the above test, I used OCMock’s -stub method instead of its -expect method. This tells the mock to handle the message and return the specified value (if appropriate), but doesn’t set up the expectation of the message being sent that the test would later verify. I can tell if the code is working based on what’s returned by the -artefactsFromCountry: method; I don’t need to worry about how it got there (although if you’re worried about hard-coding some cheat like always returning the last object in the collection, you could simply add more tests).

This test tells us something about the BNRArtefact protocol.

    @protocol BNRArtefact <NSObject>
    - (NSString *)countryOfOrigin;
    @end

And now the -artefactsFromCountry: method can be built.

    - (NSArray *)artefactsFromCountry:(NSString *)country
    {
        NSArray *artefacts = [self allArtefacts];
        NSIndexSet *locationsOfMatchingArtefacts = [artefacts indexesOfObjectsPassingTest:^(id <BNRArtefact> anArtefact, NSUInteger idx, BOOL *stop){
            return [[anArtefact countryOfOrigin] isEqualToString:country];
        }];
        return [artefacts objectsAtIndexes:locationsOfMatchingArtefacts];
    }

Conclusion

Mock objects help you to focus when you’re building applications with test-driven development. They let you concentrate on the test you’re working on now, while deferring decisions about objects you haven’t built yet. They let you concentrate on the parts of the object you’re testing, ignoring the things you’ve already tested or haven’t yet got around to testing. They also let you concentrate on your own code, replacing complicated framework classes with simple stand-ins.

And if you’ve been following the VCR analogy with your real video cassette recorder, it’s probably old enough that you can donate it to the museum where it will find a comfortable home, and a place in the inventory app we’ve just written.

The post Making a Mockery with Mock Objects appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/making-a-mockery-with-mock-objects/feed/ 0
Live and Learn: What's Your Learning Style? https://bignerdranch.com/blog/live-and-learn-whats-your-learning-style/ https://bignerdranch.com/blog/live-and-learn-whats-your-learning-style/#respond Wed, 22 Jan 2014 23:33:25 +0000 https://nerdranchighq.wpengine.com/blog/live-and-learn-whats-your-learning-style/

The Big Nerd Ranch thinker

The post Live and Learn: What's Your Learning Style? appeared first on Big Nerd Ranch.

]]>

The Big Nerd Ranch thinker

In my last post, I showed that while there’s a whole lot of stuff to learn about being a programmer, you can prioritize what to study next by comparing what you know now with two things:

  • What you need to know to help your next project;

  • What you want to know to advance your career or to improve your craft in the long term.

What’s your approach?

Now that you know what you need to learn next, the natural question to ask is _how _should you learn it? A quote attributed to Confucius says:

Tell me, and I will forget. Show me, and I may remember. Involve me, and I will understand.

Undoubtedly, some of you reading that quote will feel some affinity: you need to dive in and play with things before you fully feel like you understand them.

That’s not true for everyone, though. Some people prefer to build a mental model in their head before diving in, to understand the theory before putting it into practice. Others are happiest when they can dive in, but rather than experimenting they want to have a problem to solve. While some people would like to build something from scratch, others like to take existing code and modify it, seeing the effects that their adaptations produce.

Knowing which of these approaches works best for you can help you to decide what materials and context you need in order to get the most out of your study. Personally, I’m one of those people who prefers to understand the theory first: I learn more if I know what the problem looks like and then try to associate the solution to that model. I’m a visual learner, too, and get a lot out of diagrams and simulations: the fact that I use phrases like “looks like” and “model” in this post are little hints that show (there I go again) that this is the case.

As an aside, maybe that’s why I find the practice of test-driven development so appealing. The red-green-refactor cycle lets me describe what I know about a problem before thinking about how I should solve it.

I get to choose from a whole range of media—books, blog posts, conference presentations, podcasts and more—that will explain things in a way that suits my style of learning.

Let’s look at a specific example: say I want to learn a new part of Apple’s iOS APIs. To understand the theory and the problem that’s being solved, I would probably start by looking in the guides section of their documentation. If you learn better by experimentation, you’d be more likely to head to the sample code first. Those of you who prefer to find out what’s possible before applying it will enjoy reading the reference documentation instead.

Learning at big nerd ranch

I find it pretty amazing that Big Nerd Ranch caters to all of these different approaches to learning. The books, which form the core of the training material, cover the concepts for people like me. They also include walk-through implementations so that you can see how these things are done, as well as challenges for those of you who enjoy going off-piste and exploring on your own.

And of course it’s not just about the books: in our bootcamps, the instructors (myself included) are there to discuss the theory, the practice, our experiences of what works and what doesn’t work. To accommodate different learning styles, we use text presentations, images and even song and dance routines.

In our classes, talking to the people seated next to you isn’t considered cheating, but encouraged. When you’re facing a problem, explaining it to a peer not only helps you organize your thoughts, it also allows you and your peer to share what you understand. We even include a bit of a hike every day at our Ranches, offering time to discuss what you’ve learned with your fellow students and to reflect on the day’s teaching.

So what way do you learn best? We’ve got you covered.

Image courtesy of Andrew Jones

The post Live and Learn: What's Your Learning Style? appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/live-and-learn-whats-your-learning-style/feed/ 0
The judicious selection of what to learn next https://bignerdranch.com/blog/the-judicious-selection-of-what-to-learn-next/ https://bignerdranch.com/blog/the-judicious-selection-of-what-to-learn-next/#respond Thu, 05 Dec 2013 23:27:06 +0000 https://nerdranchighq.wpengine.com/blog/the-judicious-selection-of-what-to-learn-next/ The Universe is a big place. For someone who's keen on learning new things and absorbing information, that is both a blessing and a curse. On the one hand, it's great because it means there are many different things you could learn next. But then you encounter choice paralysis: with so many options, which one should you choose?

The post The judicious selection of what to learn next appeared first on Big Nerd Ranch.

]]>

The Universe is a big place. For someone who’s keen on learning new things and absorbing information, that is both a blessing and a curse. On the one hand, it’s great because it means there are many different things you could learn next. But then you encounter choice paralysis: with so many options, which one should you choose?

Even if you consider just professional knowledge, there’s still a huge collection of paths you could tread. Should you know more about object-oriented programming, or should you learn about functional programming? Or both? Is it time to level up your Objective-C knowledge, or should you diversify and have a go at learning Python?

Here’s how I decide what I should learn next.

Know where you came from

You can start to make sense of the options by evaluating your current level of skill. This is not straightforward—the oft-quoted Dunning-Kruger effect shows that it’s hard for novices and experts alike to objectively rate their level of skill. What you don’t know is, if you like, an unknown unknown. But it’s possible to remove that uncertainty.

Review your recent work

Take a look back at the work you were doing on your most recent project, or couple of projects. What went well? What didn’t go so well? Is there something that took longer than you expected, or was a lot harder than you thought it was going to be? Once you’ve found out where you had problems, you can start to identify what you need to know in order to keep those problems from recurring. If you get into the discipline of reflecting on your work frequently, then it need not take too long. I spend about 10 minutes at the end of each day writing a journal entry that lists:

  • What I did on this day
  • How it went
  • What this shows I should learn

Evaluation aids

You could find a chart of the skills relevant to your work, and compare your abilities to those listed. I regularly check my programming ability against the Programmer Competency Matrix (and even created my own version to address things that were missing from the original). The rules for these charts are simple. For each row, look at the skills described in the first column. If you know everything described at that level, move on to the next level in the second column. As soon as you find something you don’t know about or can’t do, stop: that’s your skill level for that row.

Ask someone else

These matrices can help you with the task of reflection, but they’re no substitute for asking people who’ve seen your work for honest, constructive feedback. This could mean asking your boss or your team-mates. If you’re a senior developer, you’ll definitely want to ask the junior programmers: maybe there’s a thing you learned to do years ago that they’ve got a newer—and better—take on. Even if you’re in a one-person company, there are still people you can talk to about your work and your skills: your clients, your contractors and your peers at your local developer group will all be able to help you.

Know where you’re going

When you’re equipped with some idea of what your current skill set is, ways to improve those skills or adopt new ones become clearer. But which path should you follow? It’s not enough to know what you can do now; you also need to have an idea of what you want to do in the future.

Tactical learning

What’s the project you’re currently working on all about? What about the thing you’ll be doing next? Are there things you could learn to make that easier? This is a good set of questions to have in mind whenever you start some new work. Perhaps you know that your next project involves a lot of 3D graphics but you need to brush up your OpenGL skills, or you’re going to add a Facebook feature to your app and don’t really know about their API. The targets you uncover by asking these questions will be short-term learning goals: they’ll help you work on the thing you’re doing right now. Regardless of their importance, understanding them could be pretty urgent.

Strategic learning

Where are you going? I don’t mean that if you’re reading this blog post on the train, then what’s your destination—I mean, what’s your plan for your career? Do you intend to be an in-depth expert in some specific technology, or more of a generalist? Do you want to become a more senior programmer, or do you want to concentrate on other relevant skills like business analysis or leading your team? Answers to these questions give you the important (though not necessarily urgent) things to learn about.

By knowing what your goals are, you can look at where you are and make a plan for how to get where you want to be. If you’re striving to take on a particular role, you can look at a job description for someone hiring for that position, and find a list of skills that are important to have. My friend Scotty from iDeveloper once gave me some good advice for thinking about this: try to write a paragraph outlining how you wish to be seen by other people in your community. Take that paragraph and decide what you need to change in order to make it a reality. This can tell you about new things you need to learn, or about things you’re currently doing that are no longer relevant to your strategy.

What’s next?

Our Chief Learning Officer, Aaron Hillegass, wrote one of the best statements about learning:

Before going any further, assure yourself that you are not stupid and that some things are just hard.

There are lots of things in the world that you do not know. This is not because you are stupid, but because there’s so much out there to learn. The first step on the path to improvement is the judicious selection of what to learn next.

The post The judicious selection of what to learn next appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/the-judicious-selection-of-what-to-learn-next/feed/ 0