Aidan Harding, who joins to talk about Object-Oriented Thinking, is a senior developer and Technology Director at Nebula Consulting Ltd.
Main Points
- OO language in the 1990s
- SOLID principles
- Inheritance and composition
- Refactoring
- Talking about “reusing the code is not the reason to break things into objects”
- OO construction
- Related resources
Links
- Aidan’s LinkedIn
- Aidan’s Twitter
- “Why every single element of SOLID is wrong” – Blog by Dan North
- “Semi-solid Principles” episode in the Two’s Complement Podcast
- Nebula Core – Apex Functional Programming episode
- Nebula Consulting Company that Aidan is working for
Contact Me
If you enjoy this podcast, please reach me out to let me know your thoughts or simply say Hello. My (Xi’s) contact info:
Take care, until next time :)!
Transcript
AI-generated, unedited.
[00:13] Hey everybody, this is Xi Xiao. This is yet a new episode of Salesforce Way podcast. Today I got a returning guest to our show. So Aidan, Aidan, how are you? I'm good. Thanks, Xi. Thanks for having me back on your podcast. Definitely. So Aidan was one of the most popular episodes in this show. So episode forty-six. Nebula Core Apex Functional Programming. That was the title of the the episode. So for any listeners, if you are interested in what is like a functional programming in general, and what is Apex functional programming, the library, Aidan wrote. Maybe you can also after listening to this episode, then go back to the episode 46 to listen to the the previous one. And Aidan, would you like to still briefly introduce yourself? Yeah, I'm technology director at Nebula Consulting. We're a Salesforce consulting partner, and. Yeah, for the upcoming conversation, it's probably relevant. My background is that I did a degree in computer science twenty five years ago, followed by a PhD, and then a bit of time working for a hardware manufacturer, actually developing an IDE for them, working on their IDE, and then eventually fell into Salesforce nine years ago.
[01:44] Excellent. In Salesforce world, we do want the compute science oriented people who can really look into the details of programming language and write those beautiful libraries to to help everybody.
[02:00] Yeah, yeah, it's definitely one of my. Missions is to convince developers in the Salesforce world to make sure that they're looking at things outside the Salesforce world, whatever their own background is. There's always lots of conversations going on around software engineering, and it's just as relevant to Salesforce as it is anywhere else. Indeed, indeed. That's also one of the missions on on this show as well. Yeah. So. In the previous episode, we talk about your library, which is the functional programming. But in this episode, you want to discuss a bit about the object-oriented, right?
[02:41] Yeah, so it's kind of reflecting on how object-oriented programming was taught when I was at university, and then what the sort of general opinions about some of those lessons are now, and all of this stuff is into kind of high-level. Conversations where different people can hold different legitimate opinions, but it feels like the weight of opinion has moved. And because I was building that functional library. In Apex, which is an object-oriented language, then kind of extensively uses object-oriented principles to build that library. So going quite far into those principles by the standards of your average Apex developer.
[03:34] And you had your university degree back in 1990s. Yeah, so it was 97 when I did 97 to 2000 was my degree in computer science. Okay. So how was that by the time in 1990s? So. I would assume C and C++ was the dominant language in the in the college. Yeah, exactly. C++ was our main language for learning.
[04:08] We would sometimes you see when we're working with microcontrollers and robotics stuff, and we did some programming in Prolog and Haskell. Just really as a bit of an exposure to kind of logic-based functional programming stuff, but it was never a main part of it for me. Hmm. And in this show, in episode 71, I had a chat with Mark Siemens about a solid principle which was proposed by by Uncle Bob. So those five guiding principle in. In object-oriented languages, do you think those things are still really solid, or do you along the time you see it more like a flexible? Like sometimes you need to adjust it according to the real life, you know. I think solid is a bit like every other decision you make as a developer. That the more Experience you have, the more you end up saying, "Well, it depends on the circumstances and the context." So sometimes solid is the right way to go, but sometimes it makes to deliberately break those principles because something else is more important. So really, there is a context
[05:39] around the problem. You have to choose. Yeah, absolutely. Yeah, but you know, for developers like me, maybe we have a bit experience already after going out from college. But the thing is solid. When I read those principles, it looks really like Bibles. They are. They can really help me to think even before writing the code. To think, okay, maybe something. The code smells. It gave me a clear message. My my code wasn't designed good. So I really really like those principles. I'm the follow it a lot. So now you are telling me right. More experience I have. I more I should think about it again. Yeah, I think.
[06:34] One of the things that is worth thinking about is there's kind of there's a model of learning that. A whole number of different people have kind of identified that as you're learning a particular area, you go through various stages of proficiency and. There's like there's an early stage where there are rules that you should apply and you just follow those rules because you don't have the experience to know how to solve the problem without the rules and that kind of gets you up to the first stage and then as you become more proficient and you've used those rules more you start to understand. The principles behind those rules and what they're getting at, and then as you get more experienced, again you start to realise that okay, these rules don't apply all the time, and maybe there are other rules that occur to me about how to blend these different concepts, or come up with my own rules. So something like the SOLID principles—it's a really useful way to kind of. Distill down a good approach to writing code, but then you will definitely see people breaking those rules. Experienced people breaking those rules, and I have good reasons for it. So you kind of have to move through those stages
[08:05] and realise that solid is not the end point. It's just a really useful thing along the way. Hmm. So real life is definitely more complex than just those principle guiding. Right. Yeah. So you can't all the used principles. There are always exceptions. Yeah. Exactly. Yeah. Could Could you give us some good examples, like what used to be thinking maybe back in the college time, people think it's good, but now along the time thinking it's it's not that good anymore. Two of the things that really stood out from university programming courses related to object-oriented programming were that object-oriented programming is all about inheritance as a way of sharing code, and also that we broke things into objects so that we could reuse them. And I think both of those ideas. Looking back on them now, are things that I would question. Like today, I would almost never use inheritance. I would always prefer. If I needed to share some functionality, I'd prefer to use composition
[09:35] instead of inheritance, and I think. One of the reasons it's difficult to wrap your head around what inheritance is and what it's for is that actually there are kind of there are three different concepts going on within inheritance, and sometimes those different concepts can be in conflict with each other. So. One of the things people mean when they talk about inheritance is they mean a specialisation. So one of the kind of canonical examples is if you've got classes to represent different shapes, then you could say a square is a specialisation of rectangle. So like rectangles are superclass and square inherits from that. And that's kind of one of the standard examples, but. One of the other concepts in inheritance is that of type substitution. So, wherever we have a rectangle, we could also have a square, and it would behave in the same way. But that example itself violates the idea of type substitution because if you had a list of rectangles, some of which happened to be squares, and you tried to set their dimensions to be two by four. Then that would be an invalid operation on a square because the two sides are supposed to be the same. So they're not behaving the same way as the subtype, and you can tie yourself in knots if you don't realise that there are these different kinds of. There are these different concepts going on, so you should probably
[11:05] only use inheritance for for either for specialization or to expect that type substitution to work properly. There's another example which is maybe simpler to understand. Here is animal is the base class and the dog is the subclass, right? Yeah, is that the first option that you just mentioned for the inheritance? Yeah, so so that is trying to express specialization. Yes, specialization inheritance. But it probably an example like that. Kind of makes sense in a textbook, but you need more context about why you decided to do that. I mean, often it'll be because there are some sort of shared operations or code sharing that you want. So.
[11:58] Depending on what it is you want to share, it might be better to share it by composition instead. And when I've I've been down the road in the past of trying to use inheritance as a way of code sharing between various classes, and often you end up with something like the superclass having a whole load of. Abstract virtual methods, which then the subclasses have to implement, and then they kind of plug into the functionality supplied by your abstract class. Well, what tends to happen there is that you find that you need to add more and more and more of these abstract kind of method hooks because the subclasses need to change the top level behaviour in more different ways, and that. Leads you into problems itself because you have this ever expanding list of hooks, and whenever you add one of those hooks, you might find yourself having to modify all the other subclasses to. Make things work. Whereas when you plug things together with composition, then there's usually an interface to define how the two classes talk to each other, and it's much more clear the communication methods. Just to let our listeners to know, in case they don't understand what's difference between the inheritance
[13:28] and the composition, I think inheritance is is a something, and the composition is like has a right. As a a simple example, the composition is like a car has a engine, so car is is composed of engine, tire, and all other components, right? So you use those components to to to to combine and then become a car. That's how I understand. Yeah, and if you want to put it into something like more kind of Salesforce terms, suppose you were writing.
[14:09] A class to access Arrest API, and that API came in two different versions, but the login method was the same. There are two different ways you could organize writing that code. You could have an abstract API class that just handles login part, and then subclasses of that for the two different versions of the API, or you could have a single class that does login. Which each of the API version classes have an instance of the login class and just delegate to that each time they need to do a login. And I would argue that having a separate login class that stands alone is easier to understand because you can just look at the interface of that thing and you know how to interact with it. Whereas the subclass superclass scenario.
[15:05] Ends up with all these hooks plugging into it, and also ends up with any of your protected or public data members in the superclass. Are available in the subclass, but to somebody reading the subclass code, it's not always obvious where these variables have come from. They have to start looking up the inheritance hierarchy and find out where all this stuff's come from. And also, I remember back in the days when I study some trigger frameworks, there are. Different layer of inheritance as well, and I really need to jump between the different layers to understand what the trigger really does. Yeah, I think that's also one of the problems. Similar problems you just mentioned for inheritance. Yeah, exactly. I think you get more encapsulation if you use composition than if you use inheritance. So. I think, you know, the the conclusion that I've come to is, almost any time I start to build inheritance, I just take a step back and say, could I do this with composition? Would it be better to do this with composition? And nine times out of ten, I end up using composition instead. For developers who are used to inheritance, how do they start to think on compositions? How do they start to model the business problem into composition way? At least for me, I
[16:35] don't know how to learn that.
[16:42] So I think. You can. It's about coming up with appropriate abstractions and being able to slice out pieces of functionality. I think one of the things that comes from the Uncle Bob school of thought with clean code is that. Within each method, we want the code within a method to be working at a single level of abstraction. So you don't want it to be doing string manipulation at one point and then immediately doing database operations at the next, because those are very different levels.
[17:23] So, that suggests to you that okay, we can wrap that string manipulation thing up into possibly another method, but probably another object that sort of encapsulate the concept of that low-level thing that you're trying to do. And if you start to wrap things up and think about them in terms of what level of abstraction you're in, then some of these. The right classes to use, the right way to compose everything, just sort of falls out as you do that, and you know your code moves through. A whole series of stages while you're writing it. That where you probably, you may well write it as one long method in the first place because at that point you don't know. What you're going to need, what abstractions would make sense, and then you might split it out into methods, and then you might see that some of those methods share the same data and share the same kind of. They all work in the same conceptual area, and say, okay, well, actually, that is an object that can stand by itself. I guess. It's something that takes experience, but one of the key other takeaways is that nobody sits down and writes a perfectly composed system in the first iteration. They move towards it and they learn more about the problem while they're solving it. Hmm. Indeed, I think
[18:53] one of things a lot of new developers hesitate to do is to refactor the code. So along the time when they write more similar functionalities, then there are really chances that you know you need to refactor your code to really think to put the some similar code into one method, or just move around and break things into different object to make the code looks. Clearer than before, easier to read. I think that's the main purpose when we do refactorings. I think it's been really transformative in Salesforce in the time that I've been working in Salesforce. The move to having a proper IDE, so Illuminated Cloud and VS Code, make it much easier to do those refactoring steps than. The bad old days when we had Dev Console, or even before that, when we were just typing text into a text box in the setup pages. You know, I just wouldn't attempt to rename something if I had to open ten different tabs to change all the references to it. But if I've got an IDE, then I can simply start to move these things around, and it gives you that freedom. If you've got a decent IDE and you've written decent tests, then it. Add so much to your ability to apply these concepts because you can refactor safely. Yeah, the things that can be done by tools then should be done by tools. For us, we are only
[20:24] thinking, right, and then we tell the tools to do the manual work. Yes, I mean I love what you said just there for for two reasons. One is that coding is thinking. My my young son sometimes plays it doing coding work in his room because he knows it's what I do. And to set up his desk for his coding work, what he got himself was. A whiteboard, a book, and something to fiddle with. He didn't even get himself a computer because he thinks that that's what coding is. Like typing is just the last bit. Indeed, and he's totally right.
[21:04] In addition to the inheritance discussion we just had, I think there's also an interesting point you mentioned to me: the reuse is not the reason to break things into objects. Could you also open this up? Yeah. So. Whenever we're reusing code in multiple places, that
[21:30] is a good thing, but it also comes with a cost that you're introducing dependencies between things that may not have previously existed when you were repeating yourself. So, to give an example, if you've got
[21:46] Salesforce implementation that's using both Sales Cloud and Service Cloud, and you've got some business logic that's kind of similar in the Sales Cloud and the Service Cloud sides of things. Then it might be really tempting to think, okay, well I can reuse that by pushing it into a single class, and then I can have some sort of parameterisation to say, am I doing the sales cloud version or the service cloud version? And you think, great, I've got fewer lines of code. That's a good thing. But you have to realise that that's come at the cost of tying together those two different things and. So that means if you get some change requests on the sales side, there's a risk suddenly that you might accidentally break the service implementation because you've tied them together with this dependency, and. Really, you need to make sure that you consider
[22:45] all the consequences before you make a decision like reusing code and. It kind of falls down that reuse makes a lot of sense for very abstract computer sciencey concepts. You know, if you have. We all reuse the standard Apex List class. If you have some fancy way of doing sorting, that makes sense because it stands alone. But as soon as you start to try and reuse a more business or domain level concept, then you are probably introducing dependencies that you shouldn't introduce and. Even at a lower level, you know, when I'm breaking an implementation up into separate classes that I'm going to put together with composition, I'm sort of treating it like I might want to reuse them because that makes sure that I have kind of clean interfaces between those components, but. Reuse isn't the reason I'm breaking them up. Clarity is the reason I'm breaking them up, so that somebody reading the code can kind of. Mentally box off the different concepts and say, okay, I know this is the bit that's dealing with handling JSON. I know this is the bit that's handling the REST service, and they all plug together like this. And I don't have to look inside that JSON box to figure out what it's doing. I can just make some reasonable assumptions, and then look in it if I really need to know the details. You know, this reminded
[24:15] me one of the. Examples in in Uncle Bob's Clean Code book. It it was a function that has a boolean. Parameter. So if the boolean is true, then the function does the logic in in way A, and if the boolean is false, then doing another way way B. So instead of putting the two different logic into one method by adding this boolean, why not to break them up into two separate? Method and then give them totally different namings, even though there there is a little bit of you know repeated codes, but it's really more clear for others to read or even for yourself to read. And you also mentioned at the different level of the business problem, the lower the reusability is better, but the higher the more abstracted, then you more give yourself a warning: should I bring things together or break them up? Yeah, definitely, and and the point that you made about that kind of boolean flag, I would always think that methods that take kind of flags as their parameter as Are a code smell that you need to be wary of, and you know that example is exactly right. That when you're calling that method,
[25:46] you know which version it is that you want to call. You know what operation it is at the point of calling, so it doesn't need to be a dynamic thing. So not only is it clearer to separate out those two concepts, it's actually faster at runtime because there's no checking of this flag. It's just. I know now what I'm doing, and I'm going to call the right thing. And you see people doing that.
[26:13] Often, people checking flags like that inside a loop can actually slow things down if you're suddenly looping over a lot of records or something. Okay, good point. Good point. In addition to this, I think one of the challenging things to write OO code back in the episode I talked with Mark Seaman as well is OO is difficult to model because there are different ways to break the problems. And then use different ways of model OOS or of creating objects to fulfill the business requirements. And I used to call it like fifty percent is art, fifty percent it's technical skill, technical implementation, and that's why Mark likes more using functional programming because it's more about mathematics. But here, what's what's your opinion? How do we really construct the the object in object oriented language such as Apex?
[27:23] I think there are a lot of parallels between programming and writing, as in writing as a journalist or as a novelist or whatever else, where it. The key thing is simplicity and readability. And when it comes to breaking your coding problem into objects, you want to break it down so that it's as simple as possible. But no simpler, and it talks in the same domain as the business problem you're trying to solve when you're at that level of abstraction. So, some of that kind of top level code you could almost show to the customer and say.
[28:24] That here is how we make the decision about how an invoice is going to get processed, and they could probably read that. You know, if they see if it's this kind of invoice, then. Do this high-level process if it's that kind of invoice. Do the other high-level process. It makes it much easier for. You to understand it makes it easier for you to convey to the customer what it is that you've built, and it makes easier for the next developer coming in to start to see the connections between the low level implementation and the actual customer requirement that they made. Have you ever seen such a good solution? After years of implementation, even
[29:15] we we do build things that way. Certainly. All of this stuff, like I say, it always depends. You don't want to go in and massively over-engineer every single solution, but there's always that point where you've done one small thing in a business process. And then another and another and your customisations kind of add up to the point where you have to take a step back and say, okay, what's the bigger picture? What are the abstractions here that are actually going on so that I can rewrite this solution into something that's going to be more manageable and. When it justifies it, when you get to that point, then that's when we would start breaking things out into objects that kind of express those high-level concepts. So, in sort of concrete Salesforce terms, you'll see. In some of our solutions, some classes which literally just encapsulate various different flavors and states of opportunities. So, you know, to be valid for a renewal in a particular customer org, there may be half a dozen different things that have to be true or within various ranges on an opportunity record, and If our customers
[30:45] asked us to do a lot of work around that opportunity, then we will encapsulate that into an object, and so you start to recode that says, if it is a new business renewal. And there will be a class for is new business renewal, and so you get that kind of flavor of it. Looks like the domain that the customer talks in.
[31:07] Is there something else you still want to share with our listeners?
[31:14] I think probably the main kind of takeaway that you might get from this is that. Everything always depends, and people who proclaim rules as being kind of cast iron rules are. Either doing it because they know that's the easiest way to help people who haven't peeked behind the rules yet, or maybe people who themselves haven't got to the stage where they realise that the rules don't absolutely always apply. And I think it's always as a developer to move yourself forward. You've always got to keep questioning. What you believe about how to do things, and keep looking for people who disagree with your current way of doing things, and keep kind of challenging yourself. Because sometimes you'll read somebody you disagree with, and you think, well, I could see their point of view, but I actually I still stick by what I had thought, and that's still a useful exercise in terms of you really understanding what it is that you think. Or it may be that they change your opinion, and the kind of the general opinion in software engineering has changed over time. So it's natural that we as individuals should be changing and trying to learn with that all the time. This sounds not only just writing code suggestions. It's like how
[32:44] we live our life as well. Yeah, definitely. Yeah, I think you you see that inclination across people who really get into development work. You know, they they always want to keep learning, right? And they do that in other aspects of their life too. Yeah, indeed. Any good study resources on the things you just learned? There was a. Very funny blog post by Dan North about why every single one of the solid principles is wrong, which falls into that category. Ultimately, I don't agree with him, but it's very thought provoking blog post. Yeah, yeah. Share me the link. I'll put in our show notes. Yeah, yeah, and. Yeah, there are a few, there are a few other bits and pieces. There is a, there is another podcast. I hate to recommend that people finish listening to Salesforce Way before they listen to this other one, but. There's a podcast called Two's Complement Podcast, and they had an episode discussing these aspects of solid principles and how much they apply in the real world. That's that's very good as well. All right, so. Aidan, just give me the link. I'll put in our show notes for our listeners to pick up, and perfect. Thanks for coming to the
[34:15] show. Thank you, Xi. Yeah, see you later. Later. Bye bye.