• You are losing out on amazing benefits because you are not a member. Join for free. Register now.
  • Big Two-Day Giveaway - Win an Amazon Kindle, a Mystery Gadget and Branded Gear. Enter Here.
  • Test your broadband speed and win prizes worth R5,000. Enter here.

Interfaces vs Abstract Classes

Spacerat

Senior Member
Joined
Jul 29, 2015
Messages
726
[)roi(];19043036 said:
Sure,
As we know, a function whose return value is determined solely by its input values and has no side effects; is always easy to test. Inversely actions without a return value, with hidden input / outputs and unmanaged side effects is very difficult to test.
Ok, agreed. Command (action)/Query (function) separation is another rabbit-hole that I find few devs have gone down...

[)roi(];19043036 said:
Simple example is how we'd go about automating a test for "WriteLine"; because whilst it's inputs are clearly defined, it has no obvious output; and its return type is "void"; yet it affects state (stdout) & can throw exceptions. Writing tests for WriteLine, or wrapping every WriteLine in a try/catch block seems like a foolish endeavour because rarely do we care if it works or not; but most actions are certainly not the same.

Surely ascertaining whether "db.executeInsertQuery(sqlQuery, sqlParameter)" worked is important; yet its design makes it inherently difficult to test. It becomes a "set up the world" scenario, where complex pre/post test actions have to accompany a test in order to recreate the "perfect world" scenario prior to testing.

These design issues are rarely contained at methods; meaning anti-test patterns most often repeat. Test Mocks are non production plugin alternatives for testing, they certainly don't address design limitations.
Ok, I get what you say and agreed.

Per definition, an action has a number of artifacts and testing should ensure that those artifacts are as expected; for 'successful' code paths as well as any kind of error condition. Testing the action by plugging in mocks for its external dependencies may yield different results, depending on the mock(s) used and how they behave. This makes the mocks more complex in that they need to somewhat represent real-world behaviour.

My software product has an extensive set of automated test cases where each has a Setup/Run/Verify/Teardown cycle. The Setup phase sets up the environment for that particular test to run. The Run phase executes the necessary action. The Verify phase verifies that the necessary artifacts have occurred for that scenario and setup. For success or particular error cases. The Teardown phase does any required de-init. Yes these tests are a huge code base on their own and is a huge, exhaustive investment. But it is one I cannot release without.

Dunno what the alternatives are though....
 

[)roi(]

Executive Member
Joined
Apr 15, 2005
Messages
5,644
Ok, agreed. Command (action)/Query (function) separation is another rabbit-hole that I find few devs have gone down...



Ok, I get what you say and agreed.

Per definition, an action has a number of artifacts and testing should ensure that those artifacts are as expected; for 'successful' code paths as well as any kind of error condition. Testing the action by plugging in mocks for its external dependencies may yield different results, depending on the mock(s) used and how they behave. This makes the mocks more complex in that they need to somewhat represent real-world behaviour.

My software product has an extensive set of automated test cases where each has a Setup/Run/Verify/Teardown cycle. The Setup phase sets up the environment for that particular test to run. The Run phase executes the necessary action. The Verify phase verifies that the necessary artifacts have occurred for that scenario and setup. For success or particular error cases. The Teardown phase does any required de-init. Yes these tests are a huge code base on their own and is a huge, exhaustive investment. But it is one I cannot release without.

Dunno what the alternatives are though....
Sure re set-up / tear-down, standard approach with stateful operations, however everything doesn't have to tied to global state.

As for alternatives, isn't that reasonably obvious? e.g. pure function i.e. contain state to the areas where it's unavoidable, and for the rest keep just keep it simple, e.g.
  • Limit function scope
  • Clearly defined input / output; same input == same output, irrespective of surrounding state.
  • Avoid partial functions (all input == valid output), or if you must delineate, clearly list acceptable input, and fail early with asserts.
 

Spacerat

Senior Member
Joined
Jul 29, 2015
Messages
726
[)roi(];19043712 said:
Sure re set-up / tear-down, standard approach with stateful operations, however everything doesn't have to tied to global state.

As for alternatives, isn't that reasonably obvious? e.g. pure function i.e. contain state to the areas where it's unavoidable, and for the rest keep just keep it simple, e.g.
  • Limit function scope
  • Clearly defined input / output; same input == same output, irrespective of surrounding state.
  • Avoid partial functions (all input == valid output), or if you must delineate, clearly list acceptable input, and fail early with asserts.
Don't see practically how you can "same input == same output, irrespective of surrounding state."

Lets say you have an Order that is in a Draft state.

If you call
Code:
Order.Post();
, the artifact is that the order goes to a Posted state i.e. state change. But
Code:
Order.Post();
again, the artifact that you expect is an error. Thus different output. Or is output != artifact in terms of your terminology? If so then yes agree.
 

[)roi(]

Executive Member
Joined
Apr 15, 2005
Messages
5,644
Don't see practically how you can "same input == same output, irrespective of surrounding state."

Lets say you have an Order that is in a Draft state.

If you call
Code:
Order.Post();
, the artifact is that the order goes to a Posted state i.e. state change. But
Code:
Order.Post();
again, the artifact that you expect is an error. Thus different output. Or is output != artifact in terms of your terminology? If so then yes agree.
It's simple, I would never define a function like Order.Post() -- because it's a clear example of an impure function; hence testing is complex.

Naturally different input == different output, but the transformation rules don't differ because same the input should always == same output. Surrounding state (in/out) is after all just hidden input / output; the problem here is that it's not defined as such. Hence I would redesign the process on the basis that the consistency of the function's transformation (x -> y) is the important part.

This is something we could easily go around in circles on; so if you want to explore this; I'm happy to take an example from you and rework it for this (probably in another thread). Alternatively I am planning to convert an OpenWeatherMap API example I setup in Java for a student to C#; where the majority of the internal transformation process is static functions.
 

Spacerat

Senior Member
Joined
Jul 29, 2015
Messages
726
[)roi(];19044114 said:
This is something we could easily go around in circles on; so if you want to explore this; I'm happy to take an example from you and rework it for this (probably in another thread). Alternatively I am planning to convert an OpenWeatherMap API example I setup in Java for a student to C#; where the majority of the internal transformation process is static functions.
Yes pls share on another thread...
 

Solarion

Honorary Master
Joined
Nov 14, 2012
Messages
17,133
Now this is the sort of thing where I get a bit upset:

Code:
string sqlQuery; // Is actually just the name of the Stored Proc eg.  spInsertEmployee
sqlQuery is the name of a string object, not the name of the stored proc. The stored proc is built in there and then the whole string is sent to the server. I see these confusing comments a lot, and it confuses me on first reading.

This is why I struggle- because of all the over-complication that takes place a lot of the time. Like someone said to me last week- this stuff is very abstract.
I see what you mean. I could have just put it in as such:

PHP:
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
//cmd.CommandType = CommandType.Text;
cmd.CommandText = "spInsertEmployee";
The reason I rather do this (See Below) is sometimes when a prospective client wants to have a demo project to click around with, he's not happy with deploying stored procs on his database. I've had it before, client is happy to run a dbscript to create the tables on his database, but the moment you mention stored procs it's like you've told him he's might catch some terminal disease or something.

I have another static class which contains all the actual raw queries themselves for such an even then just pass in a reference to it or alternately do this:

PHP:
cmd.Connection = conn;
//cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandType = CommandType.Text;
cmd.CommandText = sqlQueries.spInsertEmployee; <-- static class reference
PHP:
public static string spInsertEmployee = "INSERT INTO [dbo].[tbl_Employees] " +
"(IDNumber, ContactNo, FirstName, LastName) " +
"VALUES (@IDNumber, @ContactNo, @FirstName, @LastName) ";
**** way of doing it but sometimes that's the only option available.

Anyway, back tracking here, a hell of a lot has happened in this thread need some catching up to do.
 
Last edited:

Solarion

Honorary Master
Joined
Nov 14, 2012
Messages
17,133
What I have atm.

PHP:
public interface IJob
    {
        void InsertJob(Job job);
        List<Job> GetJobs(Job job);
    }
PHP:
    public class MyBAL
    {
        private IJob db;

        public MyBAL(IJob db)
        {
            this.db = db;
        }

        public void InsertJob(Job job)
        {
            db.InsertJob(job);
        }

        public List<Job> GetJobs(Job job)
        {
            return db.GetJobs(job);
        }
    }
Instantiating it like so

PHP:
myBAL = new MyBAL(new MyDAL());
Using it. This works btw.

PHP:
Job job = new Job();
job.Employee = Convert.ToInt32(cboEmployee.SelectedValue);
job.JobType = Convert.ToInt32(cboJobType.SelectedValue);
job.ClientName = Convert.ToString(txtClient.Text.Trim());

List<Job> JobsList = myBAL.GetJobs(job); //<-- retrieve particular job based on options selected
I'm using a List<t> because I'm going to incorporate an ICompare interface shortly to order by Employee or JobType etc
 
Last edited:

Spacerat

Senior Member
Joined
Jul 29, 2015
Messages
726
What I have atm.

PHP:
public interface IJob
    {
        void InsertJob(Job job);
        List<Job> GetJobs(Job job);
    }
As a matter of interest, why does GetJobs take a Job as argument?

PHP:
    public class MyBAL
    {
        private IJob db;

        public MyBAL(IJob db)
        {
            this.db = db;
        }

        public void InsertJob(Job job)
        {
            db.InsertJob(job);
        }

        public List<Job> GetJobs(Job job)
        {
            return db.GetJobs(job);
        }
    }
Your BAL is not adding much value. Or will that come later?

Instantiating it like so

PHP:
myBAL = new MyBAL(new MyDAL());
Ok looks like you are starting to grasp DI and abstraction

Using it. This works btw.

PHP:
Job job = new Job();
job.Employee = Convert.ToInt32(cboEmployee.SelectedValue);
job.JobType = Convert.ToInt32(cboJobType.SelectedValue);
job.ClientName = Convert.ToString(txtClient.Text.Trim());

List<Job> JobsList = myBAL.GetJobs(job); //<-- retrieve particular job based on options selected
The issue I have with this is that you are tightly coupling this code to the fact that you are using a combobox. You need to separate the presentation from the data & behaviour. DataBinding is your friend...
 

Solarion

Honorary Master
Joined
Nov 14, 2012
Messages
17,133
As a matter of interest, why does GetJobs take a Job as argument?
I should probably re-name GetJobs to something a bit more specific. OK in short, whenever someone changes a dropdown (Employee, JobType, ClientName) it changes the properties of job. I then pass this in to GetJobs to be used to filter the records in the DAL basically. It then returns the filtered list.

Your BAL is not adding much value. Or will that come later?
I've noticed that too. I was out for a walk today and thought to myself: "This BAL is so crappy (bare and empty). Maybe I should just have a DAL and call it a day."

What I might put in there is some kind of Validation for the data, and also an ICompare interface to sort some of the lists. I don't know. It just looks very bare atm.

Ok looks like you are starting to grasp DI and abstraction
That's what I needed to hear thanks man, awesome!!

The issue I have with this is that you are tightly coupling this code to the fact that you are using a combobox. You need to separate the presentation from the data & behaviour. DataBinding is your friend...
Going to have to do some more reading up on that. I know of it, but clearly have never used it, not yet.
 
Last edited:

Spacerat

Senior Member
Joined
Jul 29, 2015
Messages
726
I should probably re-name GetJobs to something a bit more specific. OK in short, whenever someone changes a dropdown (Employee, JobType, ClientName) it changes the properties of job. I then pass this in to GetJobs to be used to filter the records in the DAL basically. It then returns the filtered list.
It is important to name a class so that it reflects its intent. IMO, the argument should be a class of type JobSearchSpecification that could look like this:
Code:
public class JobSearchSpecification
{
  public string SearchText {get;set;}
  public long? CustomerId {get;set;}
  public JobStatus Status {get;set;}
  // etc
}

I've noticed that too. I was out for a walk today and thought to myself: "This BAL is so crappy (bare and empty). Maybe I should just have a DAL and call it a day."

What I might put in there is some kind of Validation for the data, and also an ICompare interface to sort some of the lists. I don't know. It just looks very bare atm.
Yes made these 'mistakes' many years ago as well. Normally a DAL would represent access to a table or other data source. Most of the time a business level transaction/action involves updates to many tables. The BLL is typically where all the changes are made for a single business level action by using the lower level (injected) DALs. Although I would not call it that, the BLL typically forms the basis for an API. This API should be a very clear and hard boundary that your UI talks to. The UI should NEVER talk to anything but the API. Then you have separation of concerns as well as a back-end that you can hook any client up to. Have a look at the architecture I suggested in one of my other posts... Read up on Domain Driven Design...

I have some serious deadlines for this week and next, but I will share some techniques for Winforms that separate presentation from behaviour in the front end (assuming you have completely abstracted the business level functionality into a set of APIs). Implementing the MVVM pattern in WinForms. Relies exclusively on binding. Databinding your controls to data and binding controls to Commands. The end result is a form with virtually no code.
 

Solarion

Honorary Master
Joined
Nov 14, 2012
Messages
17,133
My preferred option would be to let the exception just bubble up to a higher level, e.g. your BLL and then log there.

Whether you want to log at this low level or higher level, consider rather ctor-injecting an ILogger instance. This enables you to plug in any logging mechanism. File/Console/HTTP Post to JIRA/etc etc. Or even an ILogger implentation that logs to multiple loggers, e.g depending on severity.

SO if we have:
/snip
The penny finally dropped, or the light went on! ;)

Thanks Spacerat I needed to get some foundation studying behind me first before I could come back to this, and well yeah, it makes sense now, I know why now.
 

Solarion

Honorary Master
Joined
Nov 14, 2012
Messages
17,133
@Spacerat

What if I did this with your previous example. Changed it slightly and attached the Interface in the BLL. Does this make much of a difference?

Code:
public class MyBll: ILogger
{
    public MyBll(ILogger logger)
    {
        this.logger = logger;  //  <---- Contructor injection (DI)
    }

    public void Log(string msg, int severity)
    {
        try
        {
            // SomeMethodThatCanThrowException();
        }
        catch (Exception exc)
        {
            logger.Log("Message", 2);
        }
    }

    private readonly ILogger logger;
}
Code:
ILogger FileLogger = new FileLogger();
ILogger bll = new MyBll(FileLogger);
So now instead of instantiating the BLL I've simply created it as an Interface reference variable. Just experimenting here in trying to separate the UI a bit more from the BLL.
 
Last edited:

[)roi(]

Executive Member
Joined
Apr 15, 2005
Messages
5,644
@Spacerat

What if I did this with your previous example. Changed it slightly and attached the Interface in the BLL. Does this make much of a difference?

Code:
public class MyBll: ILogger
{
    public MyBll(ILogger logger)
    {
        this.logger = logger;  //  <---- Contructor injection (DI)
    }

    public void Log(string msg, int severity)
    {
        try
        {
            // SomeMethodThatCanThrowException();
        }
        catch (Exception exc)
        {
            logger.Log("Message", 2);
        }
    }

    private readonly ILogger logger;
}
Code:
ILogger FileLogger = new FileLogger();
ILogger bll = new MyBll(FileLogger);
So now instead of instantiating the BLL I've simply created it as an Interface reference variable. Just experimenting here in trying to separate the UI a bit more from the BLL.
What exactly are you trying to design? An interchangeable logger interface?
 

Solarion

Honorary Master
Joined
Nov 14, 2012
Messages
17,133
What exactly are you trying to design? An interchangeable logger interface?
I don't know, just experimenting here >.<

I just wasn't sure about this, tying the bll into the interface logic

Code:
MyBll b = new MyBll(logger);
 

[)roi(]

Executive Member
Joined
Apr 15, 2005
Messages
5,644
No worries.

BTW Interfaces are a just a way to insist on a set of conformances e.g.
Any class purporting to conform to a particular interface will be required by the type checker to provide a matching type level (signature + parameters) implementation for each of the methods set our in the interface.

Keep in mind that interfaces are a simple form of type classing: meaning we define the attributes that a certain type must have and any class implementing this must comply.

Using this for DI is not it's primary purpose, rather it is a requirement for DI to have types that all provide a common interface I.e. a common set of methods.

As for BLL or any other architecting solution for a codebase -- DI, Interfaces and Abstract classes are simply a tool to help you build a more flexible, interchangeable and testable design.
 
Top