🎙 Develpreneur Podcast Episode

Audio + transcript

The Memento pattern

In this episode, we'll be discussing the Memento pattern, a design pattern that helps you manage the state of an object.

2024-10-05 •The Memento pattern •Podcast

Summary

In this episode, we'll be discussing the Memento pattern, a design pattern that helps you manage the state of an object.

Detailed Notes

The Memento pattern is a design pattern that helps you manage the state of an object. It is useful when you need to save the state of an object and then restore it later. A Memento is a separate object that captures the state of the original object. The Memento pattern is useful in situations where you need to implement undo and redo functionality. However, it can be complex to implement and requires careful consideration of the trade-offs involved.

Highlights

  • The Memento pattern allows you to capture and externalize an object's internal state so that the object can be restored to this state later.
  • A Memento is a design pattern that helps you to manage the state of an object.
  • The Memento pattern is useful when you need to save the state of an object and then restore it later.
  • A Memento can be used to implement undo and redo functionality.
  • The Memento pattern is a way to manage the state of an object by capturing it in a separate object called a Memento.

Key Takeaways

  • The Memento pattern helps you manage the state of an object.
  • A Memento is a separate object that captures the state of the original object.
  • The Memento pattern is useful when you need to save the state of an object and then restore it later.
  • The Memento pattern is useful in situations where you need to implement undo and redo functionality.
  • The Memento pattern can be complex to implement and requires careful consideration of the trade-offs involved.

Practical Lessons

  • Use the Memento pattern to manage the state of an object.
  • Create a separate object to capture the state of the original object.
  • Use the Memento pattern to implement undo and redo functionality.

Strong Lines

  • The Memento pattern allows you to capture and externalize an object's internal state so that the object can be restored to this state later.
  • A Memento is a design pattern that helps you to manage the state of an object.

Blog Post Angles

  • The Memento pattern: a game-changer for managing object state.
  • Implementing undo and redo functionality with the Memento pattern.
  • The benefits and drawbacks of using the Memento pattern.
  • Real-world examples of using the Memento pattern.
  • Best practices for implementing the Memento pattern.

Keywords

  • Memento pattern
  • object state management
  • undo and redo functionality
  • design pattern
  • 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 episode, we are going to take a look at as we continue our software patterns exploration. We're going to take a look at the Memento pattern. This is one that you may not have heard of. It's not quite as common as some of the others that we've come across, but it is one of the software patterns out there. One of them in the original book. The software pattern is a design, so we're going to take a look at it. We're going to cover it today. We're going to start out, as we always do, with the intent of this pattern. On the book, quote, without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. End quote. Now, this is a little different from some of the other intents that we've talked about because now we're talking about doing something within object-oriented design. Now, it becomes interesting because the whole point of encapsulating internal data on a class is so that you can do what you want with it and it doesn't affect those that use it. When we want to do things like store state, store information about an object, we need to do so in a way that is not going to violate the encapsulation rules. We don't want to be able to pull stuff out of by doing a checkpoint or a save or a store of data. We don't want to be able to do that and then come back later and be able to get a hold of data that maybe we shouldn't or see data that we shouldn't or have data in a structure or a type that now is unexpected and essentially breaks everything. This becomes a little bit tricky as we actually think about the implementation as well. Now, first off, I want to talk about applying the pattern. Where would you use this? There's a lot of different places that you would use it, but the examples they come up with that really makes, I think is probably like the primary use would be if you're in a situation where you need to save the state of something because of rollbacks or undos or something along those lines. Particularly if the state is not easily returned to, if moving forward in time causes you to, unless you save state, unless you know exactly what was happening, reversing the steps may not be what you want to do. For example, if you think about a case where maybe you're modifying data, well, going backwards may not be possible because maybe you delete or break a link or something like that, that as you move forward, you've essentially broken your ability to move backwards. I'm sure you've come across some of these kinds of situations. This is a case where a memento is going to be needed. You're going to want to be able to snapshot that time, that point in time, as opposed to maybe being able to incrementally go backwards to it. Now, of course, this adds an extra little wrinkle because we want to be able to manipulate and pass around this memento, this stored saved state, saved data for a class, but you don't want to really be able to access it. You want this closed off, encapsulated shell or container of data, and you're going to have some things that are going to move it around, which they call caretakers. Then you have something else, which is ideally the thing that created it, which would be the originator that's able to actually reach into the memento and pull the data back out. You want to have something that is from the source class, source object. You want to have something where it can go in and put data and get and set data, but really nothing else can, ideally. Then it sends the memento out and there's the caretaker, which basically is able to state, maybe retrieve a state, and maybe even write it off to something that's more of a static like a, could send it out to a database or hard drive file kind of thing. You in some way actually store it, whether it's in memory or some other method, and then at some point be able to actually retrieve it as well. You have really two access types, access levels, I guess, for the memento, which again, when we get into the implementation becomes something that we really haven't seen. We haven't seen anything to this level yet. We've always, the patterns we've had, we've been able to keep them pretty simple for the most part. Not a lot of friend type relationships among classes. They're a little more hierarchical or we're able to handle it all through an interface. When you get to the actual application of it, you're going to have your caretaker, we'll start with your originator. So you've got your class and your object, and this is going to be your originator. It's going to be able to have within it a state. That state is most likely going to be, has to be somewhere in the hierarchy because you don't want the memento to be public. In some way, form or fashion, you need to have the memento to be actually a, potentially essentially a class within a class level class, a class level class. So a class that's only defined within that class. So if I start with a, let's just say, we'll call it an engine, very generic. So I've got this engine class and I want to be able to have engine state that I can store and then I can pass it around. Well, ideally within that engine, I'm going to declare a class of engine state. Now engine state is going to have to implement though an interface that the caretaker is going to be able to work with because you're going to have to be able to pass it around and do something with it. Maybe it's be as simple as like a, you know, like maybe a save and a retrieve, you know, save and load, something like that, get and set, but which does so either from memory or from sort of some sort of storage mechanism. So you have a couple of tricky little things. Now it could just be the engine state class could just be a class and then maybe it lives in the same, has the same essentially domain or package as the engine. And then you allow it to use package level access rights to be able to get to it. And of course, this is where, you know, in the past we've, we haven't had to worry about the language that you're using to implement, but in this case, Memento can be very different depending on which language you're working in, depending on whether it has the idea of or the ability to do a friend class, whether it has the ability to do package level permission setting because really public protected in private are not really going to cover it. This case, it's a little different. What we're looking for is not really, those aren't quite good enough. I mean, they will, it'll work in a pinch, but it's not ideal. So you're going to create this interface, which is the, we'll just call it like a, just think of it as like a generic Memento interface because really the caretakers should be able to deal with a lot of different types of Mementos. So if you have one that's like a, you know, going back to your example, if you have an engine Memento and there's somewhere else, you have a, let's say a calculator Memento and then somewhere else you have a skew Memento, then each of these, the interface that the caretakers deal with should be able to use the same one because really all you're doing is passing it around, save it and retrieve it. Almost like, almost a crud type of interface crud being create, retrieve, update, delete, except I don't know that you'd really need to do a delete or an update because those two would actually be done by the originator. So really all you have to do is the first two, you need to be able to, and actually even create you don't, so you need to be able to retrieve it and you need to be able to store it. So that's only two things you need to do. Anything else you would want to be able to send it to the originator and the originator is going to do that. So if there's an update, you send the Memento to the originator, they set all the values that they need to set and then pass it back. Now a key with the Memento and the originator also is that you don't have to actually store and probably will not store all of the data of the originator class. It may have 15 different properties and the Memento may only need to store one or two of them, almost like a flyweight, which we talked about in the past, we have a very small simple representation of an object. A Memento could be somewhat like that. The difference is that with the Memento, it may not be an Apple to Apple comparison. Your originator may store some information, but the Memento may have to actually have some additional information not normally stored by the originator that is required for you to actually be able to recreate that state that you're trying to store. It may be things like a global context or there may be actually other related objects or classes that the originator doesn't really need to know about, but the Memento does. It may have to have maybe some links or something like that. So a Memento, although potentially being self-contained within a class, it may also have to have some way that it could be passed around across a couple of objects. That's then where you have either the friend concept from like a C++ or the idea of something being a package level object where you would have a package level Memento and then you could have a couple different classes within that package that are able through default access able to do what they need to do. Now that can be a little programmatic of course in itself because then you could have a Memento that's got parts of its data that could be manipulated by something that you really don't want it to. So now you could get into something that's even a little more complex. You may have to have a couple of Mementos and maybe almost like a container for those or some sort of a handler or a master Memento that keeps track of all of those things so that you can properly build the context that you need to to restore whatever the given state is that you're trying to save. Now that brings us to quite a conundrum here. There are multiple ways to look at a Memento and I could spend an extremely long episode going over all of those but a lot of them would not apply. What's going to end up happening when you start looking at a Memento and consider using it there's really probably only one or two variations of a theme that are going to apply to you. They're going to really be useful to you. So instead of running through all of these options I think it's going to I'm just going to recommend instead that you take a look at examples of Memento particularly in this case with whatever language you're working in as part of the search string as part of how you're going to find some examples because they are out there. There's quite a few as far as the applications of a Memento but also how a Memento would be implemented in a given language. If you go back to the original book then it does have now granted the software patterns book was written many years ago so most of the languages that are heavily used today didn't even exist then. They didn't have a Ruby or PHP or things like that or at least not objectory with any object or any extensions. Java was just getting started. So you're going to find some good C++ examples there. There have been newer versions of the book written by other people. There's some other patterns books out there. There's some good patterns articles out there and in those cases I think you will be able to find something that's going to do a lot better job of implementing a Memento within your given chosen language. So we'll wrap it up here and get to the challenge of the week, challenge of the episode which is exactly that. Take a look at whatever your primary language is that you're working on. This time I want you to just go take a look at it. Let's do a little more research so that you're a little more comfortable with how that would be implemented within your language and most likely you're going to see some considerations or some concerns as well in building a Memento so that if you do end up going that route that you understand the trade-offs that you're going to have to choose, that you're going to have to balance against. So I'll let you get to it because that's probably a good 15, 20, maybe 30 minutes. You may want to do that on your lunch break or something like that kind of a challenge because it is also a little bit of a rabbit hole as I can easily see you start looking into it and it's just you can pretty quickly go pretty deep on how one would be used, what are some benefits of it and then some of the gotchas to look out for. So we'll free you to get back in there. No need to have a Memento for this though because you can always come back and just rerun the episode. And that being said, we'll let you get to it. So go out there, 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 Nord 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 developernord.com. Just a step forward today is still progress. So let's keep moving forward together.