Summary
In this episode, we continue our discussion of software patterns. We're getting into the specifics of the abstract factory pattern, which provides an interface for creating families of related or dependent objects without specifying their concrete classes. We'll explore how this pattern can be used to build flexible and scalable systems.
Detailed Notes
The abstract factory pattern is a design pattern that provides a way to create families of related or dependent objects without specifying their concrete classes. This pattern is useful for building flexible and scalable systems that can adapt to changing requirements. It allows you to write once and run anywhere, with minimal changes. The abstract factory pattern is a natural extension of the abstraction of the work and the data that we would normally do with object-oriented programming. It's a crucial cog in building a big, working, flexible system.
Highlights
- The abstract factory provides an interface for creating families of related or dependent objects without specifying their concrete classes.
- It's a crucial cog in building a big, working, flexible system.
- An abstract factory is a pattern that should show up over and over and over again within your applications.
- It's a natural extension of the abstraction of the work and the data that we would normally do with object-oriented programming.
- It allows you to write once and run anywhere, with minimal changes.
Key Takeaways
- The abstract factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
- It's a crucial tool for building flexible and scalable systems.
- It allows you to write once and run anywhere, with minimal changes.
- It's a natural extension of the abstraction of the work and the data that we would normally do with object-oriented programming.
- It's a crucial cog in building a big, working, flexible system.
Practical Lessons
- Use the abstract factory pattern to build flexible and scalable systems.
- Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
- Write once and run anywhere, with minimal changes.
Strong Lines
- The abstract factory pattern is a crucial tool for building flexible and scalable systems.
- It's a natural extension of the abstraction of the work and the data that we would normally do with object-oriented programming.
- It's a crucial cog in building a big, working, flexible system.
Blog Post Angles
- The abstract factory pattern: a crucial tool for building flexible and scalable systems
- How to use the abstract factory pattern to write once and run anywhere
- The benefits of using the abstract factory pattern in software development
Keywords
- abstract factory pattern
- software patterns
- flexible systems
- scalable systems
- object-oriented programming
Transcript Text
This is Building Better Developers, the Develop-a-Noor podcast. We will accomplish our goals through sharing experience, improving tech skills, increasing business knowledge, and embracing life. Let's dive into the next episode. Well, hello and welcome back. This is episode two. We are continuing on our discussion of software patterns. Actually, with episode two, we're getting into the specifics of them, as opposed to the overview that we hit with episode one. Today, we're going to talk about the abstract factory. There's not any particular reason that we start with this. It just happens to be alphabetically the pattern that shows up first. So, we're going to talk about this one. One of the things we're going to find already in this first discussion is that the factory, the patterns that we talk about will have some relationships with each other. Certain of these patterns, it makes sense to utilize other patterns as part of them. The abstract factory being part of it because you have almost implied in that the idea of a concrete factory, something that's not an abstract factory. We will actually talk about the factory pattern further down the road. The abstract factory seems to be another good one to talk about because it is a nice, another abstraction layer of what we're normally used to with object-oriented programming. This is really a logical step, I think, that we would take if we were starting from scratch, looking at the basics of what object-oriented provides and how to do it, how to properly implement an object-oriented system, application or program. And what would we do with that? How would we go from just a simple class and the abstraction ideas that come with that and move into the next step of building a system of those classes? We'll start with an abstract factory. We're going to talk about the gang of four definition. We've talked about that this many times. They have pretty simple definition. An abstract factory provides an interface for creating families of related or dependent objects without specifying their concrete classes. There's that word concrete that we'll talk about later. Now obviously, this is abstract because of its name, but it is a crucial cog in building a big, working, flexible system. If you don't have an abstract factory, then you're probably going to sort of be colluding your way through it. And like a lot of things we're going to do with these patterns and where a lot of the patterns came from is we'll take a look at the real world. Consider an automobile manufacturing plant. You have an assembly line. You've got potentially and most likely multiple models of cars that are going to be produced by that factory. The resources are roughly the same. So the platform that you're using is pretty much identical from model to model. However, how you do things within each of the steps of those processes is going to be dependent on which model of car or truck or whatever that you want to build. So let's think about a, let's say you have a small car and a mid-sized car that are coming out of it. There are going to be certain steps and certain teams that are going to work on that throughout. There will be the drive system and chassis team. There will be the engine team. There will be probably a quality assurance team. And it's most likely, although it doesn't have to be, it's probably going to be the same people working on those, depending on what the model is. So for the engine team, they're going to build one size and type of engine, and they're going to hook it up for the smaller car and a different one for the larger car. But at the end of the day, it's building, connect an engine. So from a computer programming point of view, you have a single interface. You have a single essentially command that you're giving. It's create the engine, attach the engine, create the body, connect the doors and things like that. And automobile specialists, so I'm not going to get into the details, and we don't really need to. So just considering it from a general point of view, you've got a process, you've got a series of steps. The steps are simply essentially the same, but how you implement those steps are going to be different. So you have this factory, and then within that, you essentially have concrete factories that combine to make that. If you consider a single factory doing a single model of car, and you've got multiple models, then you'd have factories within a factory. So you could think of the campus, the overall factory, as an abstract factory. And then the models, the assembly lines that you're putting together, those specific different types of cars would be a concrete factory. Now, this is not too hard to understand, I would think. And as I said earlier, it's really it's a natural extension of the abstraction of the work and the data that we would normally do with object oriented programming. They're going to build a class that has an interface to say, let's say it's just an address. So you've got an interface that allows you to get a street name, get a city, get a state, get a zip code. Now, underneath the covers, what goes on behind that may vary quite a bit. So maybe you have different address classes. Some are postal addresses. Some are maybe a PO box. Maybe there's something that translates actually out to an email address or something like that. So when you say get address, maybe it returns a street address. Maybe it returns an email address. Maybe it returns something completely different. Maybe it's a geographical coordinates or something like that. You have built this abstraction within the address class. And now we're doing the same thing with processes instead of abstracting a data model basically and some integration with that data model. We're actually abstracting a process in this case. Now, this also happens to be a pattern that is very useful. And I think you run into it quite a bit. So this is a real world programming example that you will run into on a fairly regular enough basis within frameworks and within some applications that are built with an expansion in mind. They're not one-shot applications. These are things that are built with the idea of I'm building it for a specific solution, but along the way I want to solve for the general solution as well. Maybe one of the most common examples of this would be where you have something that stores data, but the apparatus, the process that actually stores it out to something is abstracted out, allowing you to store to say a SQL Server database or an Oracle database or a MySQL database or whatever your data source happens to be. You would have an implementation of it that works with that specific back end, but essentially at runtime or configuration time, you actually define at that point what is the back end that I'm going to use. So the example we use in the post related to this episode talk about, again, just a little address book, an address book application. What if you write that and you actually may have done something like this in studies if you did an object oriented class, you may have done something along the way. And I think I've seen some tests, some skill related tests like Java tests, things like that, that are trying to get your object oriented design and skills validated and vetted. And they do something similar where you have just an address book because everybody understands that kind of an application. Well, on the back end, instead of just storing to a database, let's say you have the option to either store to say a flat text file, a database, say MySQL, or maybe it stores it out, essentially saves it by making API calls out to some other system. Well, regardless of what the back end is, they're still doing some of the, you have the same core functionality and features that would need to be provided when you're writing the code. You're going to need to load data for a given record. You're going to need to be able to save data. You're going to have to be able to load a list of data based on some sort of criteria. Maybe it's everything, every address record or maybe every address record in the state of Illinois, whatever it happens to be. You need to be able to insert or create a record, update a record, delete a record. All of these things don't change depending on the back end. They're the same functionality. It's the same features. And you really as a developer would like it to work the same no matter what. And then when it gets to that last point, sort of just in time decision of, okay, now I'm saving my data. At that point, I'm going to know whether, well, actually just the program knows whether it saves to which of those data stores. And when you create an abstract factory pattern, when you're actually using that and implementing it, the nice thing with this pattern is that regardless of the programming language we're using, if it is a good object oriented language, which is just about all of them out there that you can think of. If you think of even object oriented PHP, PHP and using the objects within it, Java, C sharp, C++, I think even Ruby, I forget exactly how it goes, but also has what's the concept of an interface. And typically the command is interface. So instead of creating a class, you would create an interface. In our example, we could create an interface of data store and that interface would have essentially abstract methods. I think it abstract in every case. Maybe there's a couple that wouldn't be, but depending on how the language is and how you'd want to do it, most likely abstract methods in every case of the ones I just listed. So creating, retrieving, updating, deleting, retrieving single, retrieving a list, those kinds of things. That would be our interface. So we'd have a data store interface that would be our abstract factory. And then we would go behind that and we would create concrete classes to, we would have like say data store text, data store XML, data store MySQL, data store Bob's API, whatever it happens to be. Let's say Amazon API. Any of those, each of those concrete pieces would be where we'd actually do the work. But when we wanted to enter, when we wanted to utilize any one of those, we'd use interface. So when we're writing the application, we're going to write it to the data store interface. And now when we actually run the application, there's going to be something at some point, there's going to be decision that's going to lead us to whichever of those. Actual concrete stores that we're going to utilize. And so when we want to do that, we're essentially, that's where we're going to create that abstract factory. So we'll have this, let's say we'll have this data store factory and it's going to give us a part of it is retrieve concrete instance. We'll just call it that. It's a nasty name, but we'll just call it that for this case. When we say retrieve, when we make that call, we're going to send it some sort of a parameter that tells us which one it's going to create. So maybe what it is, is something where we just say, get instance, maybe the application, the front end is getting a data store instance. But then that sends it back to the back end, which does some calculations and basically pumps in an instance of either a concrete instance of whichever data store that we want to use. Once you've got that, then everything that you're calling on that concrete instance is going to do it based on whatever storage, you know, let's say we pick the XML version of it. Okay, every time we make the call on the back end, it's dealing with that XML file somewhere. As far as the applications concern, everything in front of that, you know, that's on the other side of that interface, doesn't care what the back end is. That is by definition almost good object oriented design that allows you to build something initially, which is why, and we see this a lot in like Spring and some of these other frameworks. You're going to see situations where it's built with what it seems like too many layers of abstraction and data, the back end is a good example of that. The whole idea of, which we'll talk more about that actually later, of MVC, which happens to be a pattern and one we'll talk about, of course, that MVC, you would think that that's almost overkill. It's like, why do I really have to abstract the back end? Why don't I just hook that all in directly and not worry about that other layer of abstraction, that extra step that I need to take to instantiate a class or to pass through some data from the front to the middle to the back end. Well, what happens if down the road, let's say you initially build your application for MySQL and then what happens down the road, you're selling it to, let's say it's a commercial application, you're selling it to a bunch of people and you decide, hey, I need to support Oracle now. I've got a whole bunch of people that would have bought it, but because it doesn't support Oracle, they said, no, I can't buy it. You could go in and change all sorts of code or in this case, you would build a concrete class for Oracle. That's all you have to build. Think about that. You have this entire working application and now you want to flip it from MySQL to Oracle. The only thing you have to change is the abstract factory has to have the code to be able to know that I want to use an Oracle concrete data store. And then I built the Oracle concrete data store. Boom, done. I mean, that's something you could potentially do in a day or actually you could potentially do that in like an hour. And then suddenly you have a database that you support that you didn't before. That's the power of object-oriented design and especially doing it properly. Because think about that. You had this whole application could be a huge enterprise scale, massive application, hundreds of thousands, millions of lines of code that all go back in essentially hinge on this interface. Well, if you don't properly design that, if you had spillover where you weren't using that interface all the time, then everywhere there was spillover, everywhere you didn't properly do stuff, you would have to change the code now to either support, let's say MySQL that I had or Oracle. But we don't have to do that because you use this pattern because you said, you know what, I may at some point need to support another data store type. You built this interface out and you properly were using the contract of what that interface provides. You didn't go around the edges of it. You always did all of your integrations through that interface. Then changing to support a new platform is trivial. All you have to do is understand how to query and do some inserts of basic SQL in the Oracle world. Now you could even abstract behind that is maybe you've got a factory that in all of these is that we've got one that's for text files. We've got one that's for databases. One of the text files, you could have a couple different concrete implementations, one for CSV, one for fixed width, one for XML, one for some other markup language or whatever else you want to use. Binary file of some sort. On the database you could have, again, an abstract factory, but underneath it, one that goes to MySQL, one that goes to Microsoft SQL, one that goes to SQL Server, that goes to Oracle, one that goes to Postgres, one that goes to Ingress, one that goes to Mongo, whatever it is, you name it. You've got a back end that does it. And you can just like bam bam bam bam bam. You can crank out a wide area of support because you built the interface properly and because you properly respected that contract. So you don't have to go around and change code re-off. Write once, well, you write it once and then that's it. You don't have to worry about anything else. And then there's actual code reuse. All that code that you wrote is still useful. All you did was change a couple of key things and now it's off and running on a whole new platform. In the modern world, this is critical, I think. It would be foolish for you to think at this point that the application that you write has a limited lifespan. And they all do. We know, yes, it has a finite lifespan. It has a lifespan. But to think that it's only going to be used for three months or six months or even a year or 10 years may be utterly foolish. Ten years? Okay, maybe that's a bit out. But there's code running out there that was written 50 years ago. Yeah, let that sink in a little bit. What you're writing, the, you know, potentially the crap that you wrote yesterday could be around 50 years from now and somebody could be using it. Now, if you wrote it for your own little personal use, all right, the odds of that going out there and having that kind of a life are pretty limited. But if you wrote it for your company, your company may not be around 50 years from now, but if they got bought, maybe multiple times, the code that you wrote could be. Now, that's sort of awe-inspiring in a sense. I think about it, I don't know how much code I wrote back at the beginning of my career is still being used somewhere. I'm sure there's some things out there that I think there's a chance that some of that's out there. And had I to do it over again, yes, I would do it differently because I've learned a whole lot in the many, many years since I started this whole thing out. But anywhere along the way, if I have used, right, where, because I have at times used proper object oriented design, then it would be at least easy enough for somebody to come in and swap out my code for something that's more up to date. You also have to think that other than like, well, the fine one shot programs like a data import, that's a specific platform to a specific platform specifically written for your one case. And you know you're going to throw this, this application will have no use to anybody ever again. Once it gets run and you migrate to a new system. Okay, you don't have to worry about at that point. Nearly everything else I have found, and I use this when I talk to people when I'm doing the architectural and design discussions. I'll often often say, is this the only way that this can be done? Will this be the only way that this will ever be done? Database example. Oh, you want to be able to work with SQL Server. Is there, are you sure that you will never have to deal with another database that you will always be on SQL Server? If not, then I'm going to design with the idea with the hooks in there that if at some point I have to flip it to a different database, I can. And that's where the bias has come is I've basically gotten to a point where I say, unless you can tell me that this will never happen, then I'm going to leave the hooks in. I'm going to design, I'm going to use a proper object oriented design to do it that way, to do it the right way. Now, yes, there is a cost, but it's really minimal. And it pays for it the first time you have something that that does need to utilize that object oriented approach, that design. That means, yes, you know, there are cases where you don't need to use it, but you are going to almost always 99.9% of the time are going to be better off if you utilize a pattern, if you utilize the proper object oriented design. So keep that in mind as you're building stuff out. An abstract factory actually is a it's a pattern that probably should show up over and over and over again within your applications. If you have a user interface, then an abstract factory dealing with different types of interfaces should be there. What if it's a web interface versus something that goes off to a desktop interface? And what if it's responsive versus non-responsive? You know, there's things like that, like controls. How do I implement this widget in this Java framework versus that JavaScript framework versus that JavaScript framework? Things like that on the back end, the databases within the business rules. Definitely. You know, if you've got a let's say a pricing engine as part of your application, there should be an abstract factory there because your pricing engine could change dramatically. You could easily have like a and you may need the old one. So you may have like this pricing engine of 2016, of 2017, of 2018, of 2019. Maybe you have one that's based on shipping. That's the one you've got some sort of a standard one. You've got some sort of abstract factory that deals with shipping for United States Postal Service versus Federal Express versus you name the shipping company or method. These things, the real world use of an abstract factory in your programming is prevalent. You're going to see it a lot. So I guess it's turned out that this has been a pretty good pattern to start with because it's one that you should be able to go out there and like today, this week, be able to think about that and utilize an abstract factory as your solution to one of the problems, one of the challenges that you're facing. That'll wrap it up this time around. I'll let you get to it and go create a couple abstract factories out there. So have yourself a great day, a great week, and we will talk to you next time. Thank you for listening to Building Better Developers, the Developer Noor podcast. For more episodes like this one, you can find us on Apple Podcasts, Stitcher, Amazon, and other podcast venues, or visit our site at developernoor.com. Just a step forward today is still progress. So let's keep moving forward together. Thank you.