Showing posts with label Interview. Show all posts
Showing posts with label Interview. Show all posts

Resume Tips for Software Engineers in North America

Years ago as an international student preparing my resume to get my first job in Canada, I had a lot of questions and I did search a lot on how to make a resume that fits Canadian employer's requirements and style. Things have changed. Eventually, I got offers from some high tech companies and landed in a good job and now I am interviewing candidates who are like I was a couple of years ago. After looking at more and more resumes, I decided to share my experience here with a hope that will make someone's life better.


For whom this article is for? Well! for anyone looking for a new job in Canada. Especially if you are  new immigrant who has no idea about what Canadian employers are looking for, this article is tailored to your requirements. Though my experience is limited to Canada the companies I applied for are mostly US-based companies so I hope it can be applied anywhere in North America. This article is targeting only those who are in the software industry. I don't know how much it will overlap with other industries. The rest of the article is divided into two topics: 1. Resume Sections, 2. Resume Format. The first topic covers what to include and not to include in your resume and the second topic provides some formatting tips to make your resume get you a call from the recruiter.
Read More
Determine if a String has all Unique Characters

Determine if a String has all Unique Characters

Question: Implement an algorithm to determine if a string has all unique characters.

Example:
The String "Hello" has two "l"s. Therefore, it does not have all unique characters. On the other hand, if you consider the String "World", it is formed using all unique characters.

Break Down:
Let's write a class named TestUniqueness which has a method isUnique which can be called from the main method as given below:
public class TestUniqueness {

    public static void main(String[] args) {
        System.out.println(isUnique("hello"));
        System.out.println(isUnique("world"));
    }

    public static boolean isUnique(String text) {
        return false;
    }
}
Of course, you can write a fancy code to read user input and feed them to an isUnique method. However, to keep the code clean and simple, I am calling the static method isUnique and immediately print the response. The isUnique method returns false all the times. In the following section, we are going to implement the algorithm for the isUnique method.

Read More
Why should I have supertype reference & subclass object?

Why should I have supertype reference & subclass object?

Just now I've received an email from one of my student with the following question: "Why should we create Animal obj = new Dog(); instead of Dog obj = new Dog();" Of course the example given here is made by myself but the question in detail is why all use super interface or superclass reference instead of using the same class reference. This article answers the question.

The answer is: "It is the best practice being followed by our ancestors. Stop asking questions and code that way :-)" 

Ok, more seriously, consider the following example:

There is a SuperFancyClass created by developer A for some super fancy purposes. Note that all the references are HashMap.
import java.util.HashMap;

public class SuperFancyClass {
    
    private HashMap<String, Object> mapOne;
    private HashMap<Integer, Object> mapTwo;

    public SuperFancyClass() {
        this.mapOne = new HashMap<>();
        this.mapTwo = new HashMap<>();
    }

    public HashMap<String, Object> doStuff1() {
        // Do something
        return this.mapOne;
    }

    public HashMap<String, Object> doStuff2() {
        // Do something
        return this.mapOne;
    }

    public HashMap<Integer, Object> doStuff3() {
        // Do something
        return this.mapTwo;
    }

    // Some other highly complex code here
}
That class is being used by developer B as given below.
import java.util.HashMap;

public class SensitiveAgent {

    public static void main(String[] args) {
        
        SuperFancyClass superFancy = new SuperFancyClass();
        HashMap<String, Object> map1 = superFancy.doStuff1();
        HashMap<String, Object> map2 = superFancy.doStuff2();
        HashMap<Integer, Object> map3 = superFancy.doStuff3();
    }

    // Some other highly complex code here
}
Assuming both classes are highly complex but both developers A and B are happy with what they have, there is nothing else to worry.

As time progress, now developer A wants to make his Maps to maintain the insertion order. The Java API says, HashMap does not respect the insertion order so if you need insertion order, you need to switch to LinkedHashMap.

So now developer A wants to find and replace all of his/her HashMaps by LinkedHashMap. But this will not be such a short happy ending story in a highly complex codebase where there can be several other local HashMaps which cannot be replaced by LinkedHashMap. So in reality, developer A has to go and change everywhere it requires LinkedHashMap as provided below.
import java.util.LinkedHashMap;

public class SuperFancyClass {
    
    private LinkedHashMap<String, Object> mapOne;
    private LinkedHashMap<Integer, Object> mapTwo;

    public SuperFancyClass() {
        this.mapOne = new LinkedHashMap<>();
        this.mapTwo = new LinkedHashMap<>();
    }

    public LinkedHashMap<String, Object> doStuff1() {
        // Do something
        return this.mapOne;
    }

    public LinkedHashMap<String, Object> doStuff2() {
        // Do something
        return this.mapOne;
    }

    public LinkedHashMap<Integer, Object> doStuff3() {
        // Do something
        return this.mapTwo;
    }

    // Some other highly complex code here
}
But now the code of developer B collapses because of the changes in SuperFancyClass. Then developer B has to go and change the references from HashMap to LinkedHashMap as shown below.
import java.util.LinkedHashMap;

public class SensitiveAgent {

    public static void main(String[] args) {
        
        SuperFancyClass superFancy = new SuperFancyClass();
        LinkedHashMap<String, Object> map1 = superFancy.doStuff1();
        LinkedHashMap<String, Object> map2 = superFancy.doStuff2();
        LinkedHashMap<Integer, Object> map3 = superFancy.doStuff3();
    }

    // Some other highly complex code here
}

Rule of debugging:

Now, this is the time to introduce an important rule of debugging:
Fixing a bug is equivalent to introducing new bugs.
In other words, if you modify existing code, the number of changes you have made is proportional to the number of new bugs introduced in the code because we are all human and we do mistakes. Without our intention, we might delete a local variable that hides an instance variable. Or else we might modify that single line which brings the entire world to an end. So always keep your changes as less as possible.



Coming back to the previous scenario, developer A modifies the SuperFancyClass in 7 places and developer B has to modify the SensitiveAgent in 3 places. Even in a code-base which contains two dummy classes, there are 10 places altogether to modify. Just imagine a project with hundreds/thousands of classes and millions of lines. Yep, they do exist and it will be catastrophic.

Time to travel back...
Suppose if the developer A designed his/her class as given below using super interface references (However the objects must be created using a subclass. See the constructor):
import java.util.Map;
import java.util.HashMap;

public class SuperFancyClass {
    
    private Map<String, Object> mapOne;
    private Map<Integer, Object> mapTwo;

    public SuperFancyClass() {
        this.mapOne = new HashMap<>();
        this.mapTwo = new HashMap<>();
    }

    public Map<String, Object> doStuff1() {
        // Do something
        return this.mapOne;
    }

    public Map<String, Object> doStuff2() {
        // Do something
        return this.mapOne;
    }

    public Map<Integer, Object> doStuff3() {
        // Do something
        return this.mapTwo;
    }

    // Some other highly complex code here
}
The developer B would develop his/her code like this because the return types of those methods are Map; the super interface:
import java.util.Map;

public class SensitiveAgent {

    public static void main(String[] args) {
        
        SuperFancyClass superFancy = new SuperFancyClass();
        Map<String, Object> map1 = superFancy.doStuff1();
        Map<String, Object> map2 = superFancy.doStuff2();
        Map<Integer, Object> map3 = superFancy.doStuff3();
    }

    // Some other highly complex code here
}
Now if the same situation comes where the developer A wants to preserve insertion order in his/her maps, only lines he/she has to change are just those two lines inside the constructor as shown below:
import java.util.Map;
import java.util.LinkedHashMap;

public class SuperFancyClass {
    
    private Map<String, Object> mapOne;
    private Map<Integer, Object> mapTwo;

    public SuperFancyClass() {
        this.mapOne = new LinkedHashMap<>();
        this.mapTwo = new LinkedHashMap<>();
    }

    public Map<String, Object> doStuff1() {
        // Do something
        return this.mapOne;
    }

    public Map<String, Object> doStuff2() {
        // Do something
        return this.mapOne;
    }

    public Map<Integer, Object> doStuff3() {
        // Do something
        return this.mapTwo;
    }

    // Some other highly complex code here
}
Developer B does not need to change anything because there is nothing changed in the API level (return types are not changed by developer A). So now the same behavior is achieved with less number of modifications (This time only 2). This is what we call extensible, modifiable, (all those blah-blah-blah-able) code.

Then why not Object instead of Map?
Now you may have a question: Should we always use the super most interface/class as the reference? Not always. It depends on the requirements. Climbing towards super types means, we are limiting the features because 99% of the time, subclasses have more features than supertypes. For example, compare java.lang.Number with its subclasses or compare java.util.Collection with its subinterfaces/classes. So use the supertype reference which has all the functionalities you are expected to have.

Is the rule applied to local variables?
Then what is the advantage of using super reference in local variables which have no impact on others? I guess this is because we developers are used to that and there is nothing wrong with following the same practice in local variables. For example, consider this case:
import java.util.List;
import java.util.ArrayList;

public class HelloWorld {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Java");
        list.add("Python");
        list.add("C++");
        System.out.println(list);
    }
}
In this code, there is no advantage of using super interface reference but I do write this way because I am used to it. If there are no disadvantages, why do you bother with it? Keep calm and code your references in supertype :-)
Read More

Contact Form

Name

Email *

Message *