"The Clean Code Talks -- Inheritance, Polymorphism, & Testing"

By: GoogleTechTalks

2134   70   332303

Uploaded on 12/05/2008

Google Tech Talks
November 20, 2008

ABSTRACT

Is your code full of if statements? Switch statements? Do you have the same switch statement in various places? When you make changes do you find yourself making the same change to the same if/switch in several places? Did you ever forget one?

This talk will discuss approaches to using Object Oriented techniques to remove many of those conditionals. The result is cleaner, tighter, better designed code that's easier to test, understand and maintain.

Speaker: Misko Hevery

Comments (6):

By anonymous    2017-09-20

Instead of that dreadful abomination, you should learn how to utilize spl_autoload_register():

spl_autoload_register( function( $classname ){

    $filename = 'inc/classes/' . $classname . '.class.php';

    if ( !file_exists( $filename) ){
        throw new Exception("Could not load class '$classname'.". 
                            "File '$filename' was not found !");
    }

    require $filename;

});

And you should register the autoloader in your index.php or bootstrap.php file, and do it only once per loader (this ability lets you define multiple loaders, but that's used, when you have third party library, which has own autoloader .. like in case of SwiftMailer).

P.S. please learn to use prepared statements with MySQLi or PDO.

Update

Since you are just now learning OOP, here are few things, which you might find useful:

Lectures:

Books:

Original Thread

By anonymous    2017-09-20

Since you request a way to mock a DataContext I assume that you really want to do some unit tests and not integration tests.

Well, I will tell you how to accomplish this, but first I would like to encourage you to read the following links, they are all about writing clean testable code.

And check the links from this response:

Watch the clean code talks from Misko Hevery (given to the Google people)

One thing that I used to repeat to myself and to my fellows at work, is that anyone can write a unit test, because they are silly easy to write. So a simple test is essentially all about making some comparisons and throw exceptions if the results fails, anyone can do that. Of course, there are hundreds of frameworks to help us write those tests in an elegant way. But the real deal, and the real effort shroud be put on learn how to write clean testable code

Even if you hire Misko Hevery to help you write tests, he will have a real hard time writing them if your code is not test-friendly.

Now the way to mock a DataContext objects is: do not do it

Instead wrap the calls using a custom interface instead:

public interface IMyDataContextCalls
{
    void Save();
    IEnumerable<Product> GetOrders();
}
// this will be your DataContext wrapper
// this wll act as your domain repository
public class MyDataContextCalls : IMyDataContextCalls
{
    public MyDataContextCalls(DataClasses1DataContext context)
    {
        this.Context = context;
    }

    public void Save()
    {
        this.Context.SubmitChanges();
    }

    public IEnumerable<Product> GetOrders()
    {
        // place here your query logic
        return this.Context.Products.AsEnumerable();
    }


    private DataClasses1DataContext Context { get; set; }

}

// this will be your domain object
// this object will call your repository wrapping the DataContext
public class MyCommand
{
    private IMyDataContextCalls myDataContext;
    public MyCommand(IMyDataContextCalls myDataContext)
    {
        this.myDataContext = myDataContext;
    }

    public bool myDomainRule = true;

    // assume this will be the SUT (Subject Under Test)
    public void Save()
    {
        // some business logic
        // this logic will be tested
        if (this.myDomainRule == true)
        {
            this.myDataContext.Save();
        }
        else
        {
            // handle your domain validation  errors
            throw new InvalidOperationException();
        }
    }
}

[TestClass]
public class MyTestClass
{
    [TestMethod]
    public void MyTestMethod()
    {
        // in this test your mission is to test the logic inside the 
        // MyCommand.Save method
        // create the mock, you could use a framework to auto mock it
        // or create one manually
        // manual example:
        var m = new MyCommand(new MyFakeDataContextFake());

        m.Invoking(x => x.Save())
            //add here more asserts, maybe asserting that the internal
            // state of your domain object was changed
            // your focus is to test the logic of the domain object
            .ShouldNotThrow();

        //auto mock example:
        var fix = new Fixture().Customize(new AutoMoqCustomization());
        var sut = fix.CreateAnonymous<MyCommand>();
        sut.myDomainRule = false;

        sut.Invoking(x => x.Save())
            .ShouldThrow<InvalidOperationException>();
    }

    public class MyFakeDataContextFake : IMyDataContextCalls
    {
        public void Save()
        {
            // do nothing, since you do not care in the logic of this method,
            // remember your goal is to test the domain object logic
        }

        public IEnumerable<Product> GetOrders()
        {
            // we do not care on this right now because we are testing only the save method

            throw new NotImplementedException();
        }
    }
}

Notes:

  • When you declare your IMyDataContextCalls interface you are actually abstracting the use of a DataContext, therefore this interface should contain only POCO objects (most of the time), if you follow this approach your interfaces will be decoupled from any undesired dependency.

  • In the specific MyDataContextCalls implementation, you are explicitly using a DataClasses1DataContext context, but you are free to change the implementation at any time and that won't affect your external code, and that's because you are always working with the IMyDataContextCalls interface instead. So at any time you could change for example this implementation for another one using the wonderful NHibernate =) or the poor ef or a mock one

  • At last, but not least. please double check my code, and you will notice that there are no new operators in the domain objects. This is a rule of dumb when writing test friendly code: decouple the responsibility of creating objects outside of your domain objects


I personally use three frameworks on every project and on every test I write, I really recommend them:

For example, in the code above, I showed you how to write a manual fake for your repository, but that clearly is something we do not want to do in a real project, imagine the number of objects you would have to code in order to write your tests.

Instead use the power of AutoFixture combined with Moq:

This line: var m = new MyCommand(new MyFakeDataContextFake());

Will become:

        var fixture = new Fixture().Customize(new AutoMoqCustomization());
        var sut = fixture.CreateAnonymous<MyCommand>();

And that's it, this code will automatically create mocks for all the objects needed in the constructor of MyCommand.

Original Thread

By anonymous    2018-06-22

You apparently do not understand polymorphism. This might help: https://www.youtube.com/watch?v=4F72VULWFvc

Original Thread

By anonymous    2018-06-22

I liked Lino's answer to use DoubleBinaryOperator .(+1) for that. Most ifs or switch can be replaced by polymorphism . Functions without ifs/switch are easier to read/test and maintain . Greatly influenced from this google talk https://www.youtube.com/watch?v=4F72VULWFvc You can use this ENUM version as well . You need JDK 8 for this

public enum Operation {

    PLUS("+", (x, y) -> x + y),

    MINUS("-", (x, y) -> x - y),

    MULTIPLY("*", (x, y) -> x * y),

    DIVIDE("/", (x, y) -> x / y);

    private final DoubleBinaryOperator op;
    private final String symbol;

    public double apply(double x, double y) {
        return op.applyAsDouble(x, y);
    }

    Operation(String symbol, DoubleBinaryOperator op) {
        this.symbol = symbol;
        this.op = op;
    }

    public String toString() {
        return symbol;
    }

    public static void setAnswerText(double num1, double num2, Operation operator) {
        final double ans = operator.apply(num1, num2);
        final double doub = Math.round(ans * 100000.0) / 100000.0;
        if (doub == (int) ans) {

            Answer.setText(Integer.toString((int) ans));
        } else {

            Answer.setText(Double.toString(doub));
        }
    }

    public static void main(String[] args) {
        final double num1 = 100;
        final double num2 = 200;
        final Operation oper = Operation.PLUS;
        setAnswerText(num1, num2, oper);

    }
}

Original Thread

Popular Videos 6

Submit Your Video

If you have some great dev videos to share, please fill out this form.