Created with Sketch.
Develpreneur: Become a Better Developer and Entrepreneur
14 minutes | 20 days ago
Object-Oriented Design : Invest In The Design Process
We wrap up the season with a focus on the design process. There is a cost to doing this, as with most things of value. However, you will find that the benefits in tighter, easier to maintain, and higher quality code are worth the investment. We can use the tools and frameworks we have to take shortcuts to an object-oriented solution or be better by spending time on design. Object-Oriented Design Is A Revision We have discussed how the solution is always our primary focus. Developers solve problems. Therefore, our "why" is crafting a solution. The emphasis is on the solution not on the craft. Once we have a solution we are comfortable with, then we can look to mold it into something better. Therein lies the rub. We have a solution so we could push forward to implementation. On the other hand, we can spend time refining the solution. The wise developer chooses the latter approach. The Design Process Is An Investment There is no question that design time is an investment in our implementation. A brute-force approach is often an option. However, those solutions rarely hold up over time. Even duct tape has its limits. Thus, time spent on design assumes we will be able to make up that time in future endeavors to maintain, scale, or enhance our implementation. That makes efficiency and clarity the primary goals for our design process. We want to craft code that is worth re-use and that can handle relying on it for bigger and better things than this one solution. Stopping When Needed We also are going on hiatus for a while with the podcast. This is going to be our last episode until further notice. It does point to the value of making decisions and acting once a decision is made rather than some arbitrary time frame. We often talk about goals and regular reviews of our plans. However, sometimes it is best to act sooner rather than later. That can help us avoid investing in things that we see of lesser value.
16 minutes | a month ago
Keep It Simple - Avoid Complex Object-Oriented Designs
Software developers are known for many traits. Unfortunately, one of those is ignoring the keep it simple philosophy. We tend to "show off" our prowess through complex designs and implementations. However, those rarely are the best ones in terms of maintainability and even performance. Simplicity has its benefits. Keep It Simple - Direct Is Better While there is a value in the "scenic route" when we are out for a leisurely drive, it is not the most effective way to travel. The same concept applies to our designs and solutions. The direct solution is often the best, even though it may sacrifice scalability or its ability to be applied in a general fashion. When we focus on highlighting our design skills, we often end up with a solution looking for a problem. That is not a mindset that servers our customers. Solve The Problem First Therefore, we should always start our design with a focus on the problem. The starting point should be the solution and then map that to an object-oriented design. If we focus on the OOD, we may inadvertently attempt to cram a square peg in a round hole. This thought takes us back to the agile manifesto. The number one goal of our solution should satisfy the customer. While a solid design is valuable, it is a means to an end, not the end itself. Opportunity-Based Design When we start with a solution and then translate it to an object-oriented form, we are taking advantage of opportunities. There are opportunities to improve our solution through OOD. A good design makes use of these. Thus, our solution dictates where OOD may be useful. We can take advantage of those without using all of the features OOD provides us. It is like many things in life. Just because we can does not equate to "we should." For example, our car can go faster than the speed limit. However, there are many cases and reasons not to push the car beyond that limit.
15 minutes | a month ago
Leverage Your Language - Use What It Gives You
An often overlooked facet of object-oriented design is how to leverage your language to speed development. Every coding language has features and strengths to make your design or implementation better. We should not design to a language initially. However, we can tailor our implementation and detailed design to take advantage of these features. Leverage Your Language to Boost The Implementation Languages are implementation tools. Therefore, they come into play during that phase. Nevertheless, you can leverage your language as you consider the details of the design. Some approaches will either be simple or complex based on the language. We can include the concept of interfaces or multiple-inheritance in a hierarchy. That includes more granular steps like patterns built into the language. We often see these in the form of iterators or factories. Keep Up With Changes While the initial design should leverage your language, enhancements and upgrades should incorporate the same. Be aware of the changes to a language or environment over time. That includes fixes, tweaks, and new versions. A problem that may have been overly complex to solve in the past could be a "freebie" solution now that it is handled in a single line of code. Languages evolve and often do so with a focus on eliminating weaknesses. That is due to developers suggesting features based on their pain points. We often see a language that is limited in use during early versions become more general-purpose over time. The developer is the customer, so the software team or company will cater to the developers. The things that are difficult to use early on will be simplified or greater support provided. Under The Covers The changes to be aware of include those that are "under the covers." These types of changes are much more implementation-impacting. However, version upgrades have been known to make vast improvements in persistence, multi-threading, and thread-safety.
16 minutes | a month ago
Finding Balance In Your Object-Oriented Design Complexity
There are many areas of life where moderation is required. Object-oriented design complexity is one of those situations. We can create a solution that is highly granular and flexible or one that is monolithic and simple. However, as is often the case, the best solutions find some middle ground that keeps our application flexible and limits complexity. A Balanced Object-Oriented Design Every solution is unique and has its own constraints and requirements. Nevertheless, we have guidelines to avoid a solution that is either over-simplified or over-engineered. They may appear obvious but are often overlooked. Isolate unique functionality - we will not get much re-use from it. Look for repeated actions or manipulations. Focus on a unit of work. Do not break down a problem that is not improved by doing so. When we walk through the steps of our solution, we should be either to easily see how to solve the related problems. Those related problems are often a pointer to our methods and classes. We want to break it down into easily solvable pieces and then stop. There is no need for our object-oriented design complexity to rise to the level of micro-managing the solution. Re-use Is A Many Splendered Thing When we are thinking about code re-use and the related work to make a solution generic, there should be multiple uses. This should be more than two occurrences or at least a vision of the code being reused many times. That is where we gain back the extra time spent in creating a generic solution rather than brute force. Our design should take into account that the direct and simple approach can also be the best. We do not get points for an elegant solution in most cases. Simple First The best designs start with a simple solution. It is easier to make our software more complex in a future iteration than simplifying it. We also will have lower maintenance costs associated with a less complex solution. These benefits add up and should bias us away from over-architected solutions. Not only are there no gains inherent in a complex system, but there are also many assumed costs. These include a slower solution, one that is harder to maintain, and a longer ramp-up time for new/additional resources. Therefore, simple is better than complex when possible.
14 minutes | a month ago
Documentation Of Our Object-Oriented Solution
It is hard to think of a facet of software development that programmers dislike more than documentation. This task is almost always pushed to the end, and shortcuts are used wherever possible. However, we also are quick to complain about a lack of documentation when we pick up others' code. Maybe we need to give so we can get. that is the focus of this episode. Documentation Begins With A Signature All good documents start from an outline. There is either a written or mental plan that the author follows. In software, this objective comes from the signatures of the class and methods. We have a set of details we must provide for every method out there. What are the parameters supported? Are there constraints for those parameters? What is returned? Are there exceptions or error-handling that needs to be documented? Do we require other classes or libraries? Are there side effects? That is a sizable list when you consider that many methods are documented in a sentence or two. While documentation can be exact and concise, that is not always best. Using Documentation Generators Consider a method: integer DoSomething(parm1, parm2) Here is the typical documentation: DoSomething returns an error code or 0 on success. The first parameter is a number that tells which type of processing is needed (trim, pad, compress, encrypt). The second is a string that is processed. I am being a bit obtuse in the above comment, but not far from what most automated tools generate. That should be a start for our documentation and not the final version. Add Color Commentary We need to add details and specifics to our documentation. This includes things like validations, side effects, and avoiding magic numbers. Therefore, a better approach to the above would be something like this. DoSomething returns a 0 for success, -1 if the action is invalid, -2 if the string is null, -3 if the action fails. The first parameter (parm1) is an integer with a value of 0 to 3. This tells the method the action to perform. 0 - trim the string 1 - pad the string (left pad spaces to make the length 20) 2 - compress the string 3 - encrypt the string The second parameter (parm2) is the string the action is performed on. The resulting string is set in the instance and can be viewed through the getValue method (provide a link to that method if possible). There is minimal formatting in the example above. Nevertheless, notice how much more information we have now provided. There is no guesswork, and our expectations are properly set. Thus, we have a document that is useful and will make the next developer happy to work with our code.
15 minutes | a month ago
Test-Driven Development - A Better Object Oriented Design Approach
Testing and Design are often at opposite ends of the software development life-cycle. However, test-driven development is an excellent way to drive our design. It can help us build better classes and improve re-use. This episode focuses on TDD and how it can point us to full-featured classes with better error handling and messages. Test-Driven Development For Design We previously looked at unit testing and class-level quality assurance. While those are important tasks once we get the class implemented, they are better incorporated into the design process. When we do, it ties our testing (validation) more directly to requirements. Thus, we have goals for each bit of code to achieve. We drive our implementation by the tests we need to pass. This approach is not the only way to do so. However, it does translate requirements to the implementation. It also can help us find gaps. Testing Responses Testing centers around the responses and behaviors of our classes and methods. Thus, our class must support a message, exception, or value that the test is looking for. In the case of exceptions, I find testing a key indicator of where we need to support them or report errors. That is because a unit test has this basic format. Make a call. Gather results. Compare results to a set of assumptions. That third step will highlight areas where we have the proper amount of error handling and messages supported. A Simple Example Let's consider a simple example to show how this works. In our example, we have a method that takes two numbers, adds them, and returns the sum. The requirements tell us we need to verify the sum is correct; we warn the user if a negative value is sent. Finally, we need to warn the user if they do not provide a number. If we implement first and then test, we can easily forget the latter requirements. We will probably add the numbers and return the sum. It is not until we get to testing that we will realize we have some exceptions handle. If we take the test-driven development approach, then handling these exceptions and related messages will be in our minds from the start. That can heavily influence the kinds of things we want to support and return as we design our class.
14 minutes | a month ago
Testing Object-Oriented Code - A Better Design Approach
We have spent a lot of time on the core concepts of object-oriented design and programming. However, testing object-oriented code is at least as essential to our practical approach. We need to ensure that our code is written with a quality that makes it worth re-using. Otherwise, it is a better use of time to write new code instead of wrangle low-quality code with the idea of re-use. We have all seen these situations where it is easier and faster to start from scratch than building on previously written code. Testing Object-Oriented Code Through Design We will focus on test-driven development in the next episode. However, our implementation and approach to a solution can be improved with the inclusion of unit tests. There is a level of expectation that unit tests point to. When we start to code these, the gaps in our return values and messages become apparent. Thus, we get the benefits of tested code and a more thorough design. Unit and Class-Level Testing Unit-testing typically is at the method level. The good news is that we can connect a few of these and easily shift to class-level tests. These are important steps towards building a highly re-usable class. We will show (through successful tests) that our methods work individually and as part of the class system. While interactions and interfaces for our class are important, it is more valuable to have a class that is a solid ecosystem. The Goal Is Re-Usable All of these points lead us to how confident we are in our code. If we have a low-confidence, then why would we re-use it? In those cases, we are better off writing "one-off" code that avoids the costs of object-oriented. There is no benefit in staying on the fence. Either embrace OOD and testing object-oriented code for re-use or write something quick-and-dirty. This concept applies throughout the real world. There is no sense building on a flimsy foundation.
15 minutes | a month ago
Destructors And Cleaning Up An Instance: Object-Oriented Design
When we get to the end of our use of a class instance, we need to clean up after ourselves. This is where destructors come into play. They are similar to constructors in a mirror fashion. While a constructor creates an instance, a destructor removes an instance. Destructors And Resource Release Many languages have a default destructor, much as they have a default constructor. These methods likely free up some pointers behind the scenes. However, we also have resources and properties or even notifications that may be needed as part of this process. That leads us to a process that effectively undoes the creation and initialization steps. We first release resources (reverse the initialization). Then we remove the instance from memory (reverse the constructor). The Important Properties The challenge with a destructor and cleanup is that many languages do most of this work for us. The tasks of memory initialization and pointer creation are done under the covers. Therefore, we can leave the default destructor as is and let the system manage those things. However, we do often have non-simple attributes like child instances and resources to release like file handles. These complex attributes and initialization steps are critical to review and address in our destructors. Keep It Simple With all of the above in mind, we also need to consider what happens when a destructor fails. Therefore, it is helpful to have a close or cleanup method that we can call separate from the destructor. It may seem like splitting hairs, but there is value in handling releasing resources separate from destroying an instance. This impact comes from destructors often being called more often than we think. A fat destructor can dramatically slow performance, just as we see in fat constructors. This multiple-step approach to cleaning up makes it easier to handle each step at a time. Thus, potential exceptions are addressed individually rather than treating them in a bunch. That smaller scope will help you avoid headaches and synchronization of getting the system cleaned up.
15 minutes | a month ago
Constructors And Initializers In Object-Oriented Design
When we create an instance of a class, we often need to do some initialization. Constructors and initializers achieve this goal. These are two different ways to get our data into an instance and are an essential part of our design. In this episode, we examine the design decisions we need to make as we decide how to use these common methods. Constructors And Initializers - The Distinction A constructor and an initializer method can be thought of in very similar ways. However, there is one particular trait that differs between the two approaches. A constructor returns an instance of a class, whereas an initializer sets values on an instance. This may seem to be a minor difference. It is not. There are situations where we need to create many instances of a class, and a "fat" constructor can lead to performance issues. The creation of an instance is an important operation. Thus, it is best to complete it in as short a time as possible. That is where an initializer comes in. We can create an instance quickly and then take our time populating that instance (i.e., initializing it). A No-Args Constructor Many languages include a default constructor for classes that takes zero arguments. This provides a quick way to get an instance. I personally find these important and highly useful in extending classes. The exception to providing a default constructor is when you need to provide limited instances or some level of instance management. Adding Parameters Once you start adding parameters to your instance creation, you should consider an initializer. That provides a cleaner way to create an instance and then populate it. You also give the developer the ability to "lazy load" data, for that instance. That is an important option in large systems and large classes. A good initialization routine also provides an opportunity for a mirroring cleanup method that makes it easy for a developer to control resource usage within classes. A few core parameters for an instance may be convenient, but once that list gets long, it can slow the process. It is better first to get a valid instance and then do something with it.
14 minutes | a month ago
Static Attributes And Methods In Object-Oriented Design
In this episode, we look at how to use static attributes and methods in our class design. These are important but can be over-used. They can even be a supported way to break the object-oriented nature of our program. We can "static" our way into a design that can not be extended. Therefore, we need to use these sparingly and with consideration about how they may limit our solution. Static Attributes It always helps to set the stage for these types of discussions. Static attributes and methods are not supported by all languages, nor is that always the proper terminology. For our purposes, a "static" is a class level value or function. Thus, there is only one version of these items. There is not a version on each instance. For example, a class-level attribute of "counter" will have the same value for all instances. When the counter variable is altered, all instances see that new value. As you can see, this approach will tie our instances together in a loose way. They are now linked through the static properties. The Singleton The software pattern "singleton" is an example where static values and methods are useful. They are almost required to implement this pattern properly. In this case, we have a single instance only for a class. This could be to provide global values or to limit access to resources. We might even blur the singleton into a limited number of instances. This option is a great way to have a small number of files open or database connections. A Static Anti-Pattern One of the uses of a static value is as a way to pass around or share that value. This approach can be used to avoid method parameters or a data store. While that is a valid way approach, it can be limiting and error-prone. A static value opens us up to the idea of race conditions. What happens when two bits of code try to update the value at the same time? It will be cleaner for us to pass values and limit access through properly designed methods, even though the direct approach is likely going to require less code. This feature provides us a lot of power. However, with great power comes great responsibility. Design responsibly.
16 minutes | 2 months ago
Software Design - Finding Balance With Coupling And Cohesion
We have spent a few episodes looking at cohesion and coupling. Now it is time to find balance in these two software design approaches. Each approach has strengths and weaknesses. Therefore, our best approach is to combine the strengths while offsetting the weaknesses. This best-fit approach is not as difficult to achieve as it may seem. Cache The Main Data The large object approach allows us to access data and methods quickly. We have a short path to get to these items as the layers of abstraction have been minimalized. The greatest value of this approach is when we have data or methods that are often used. There is little value to this efficiency for items we rarely utilize. For example, think about a daily report vs. a monthly report. The calculations that generate the monthly report can be less efficient and time-consuming because they are rarely utilized. A cost of 15 minutes for a feature is only paid once a month. The daily report has a higher value of performance. That same addition of fifteen minutes adds up to a loss of over a day per month. More iterations equate to a greater value for performance improvements. The A La Carte Approach We can push our primary data and features into a few large objects. That helps us with performance. However, it may seem overly taxing to a system to load all of the lesser-used data and methods in a single class. Instead, we can utilize smaller objects that can be quickly instantiated and freed for less frequent needs. The overhead cost of initialization and cleaning up these small objects is often a wash. We do not use them much. Thus, the slower performance has little overall impact on our system. A Flexible Software Design We pointed to flexibility as a strength of the coupling approach. However, we did not mention that coupling can be done with a combination of large and small objects. We see this thought process played out in patterns and frameworks like the flyweight and object factories. Even big things can have some small (replaceable) moving parts. That is the key to the blended approach. Keep the common things close and store the less-common features in easy-to-initialize small objects.
17 minutes | 2 months ago
Coupling - A Smaller Object Approach To Software Solutions
In object-oriented software, we talk about the concept of coupling. Put simply; this is a small object approach to our solution. It is the opposite end of the spectrum from large-object, or monolithic, designs. In my experience, a developer's bias towards cohesion or coupling often comes from their background. Those that have come from functional languages and platforms are more comfortable with cohesion, while those that built components tend to use coupling. Coupling Strengths It is hard not to point to flexibility as a primary strength of this approach. It stands to reason that the more points of adjustment available, the more flexible the solution. This example is easy to see in physical objects. Remove all of the joints in your body, and you will be less flexible. Likewise, return to our Lego example and consider what you can build with smaller as opposed to larger blocks and structures. Smaller Impact Another important strength of coupling is the ability to make changes with a smaller requirement of validation. Thus, we can make improvements or changes and have less testing required. This benefit can be critical to maintaining large systems without endless regression tests and unworkable release cycles. We do need to verify we have not introduced unintended changes. However, we have the ability to make tighter changes that are easier to debug. Where Does This Go? The challenge with coupling is figuring out what goes where. This conundrum is not different from the levels of normalization in a relational database. There are situations where an attribute or method has overlap in the classes that use or need it. This straddling of classes can make it challenging to place our bits of code properly. On the other hand, those situations often point us to where a third-class shared by the two primaries is the best solution.
16 minutes | 2 months ago
Cohesion - A Big Object Approach To Design
This episode focuses on cohesion and the idea of using fewer large objects to solve a problem. This approach is often considered the opposite of using more smaller objects. We will find out that there are ways to combine these two different approaches as a best-fit method. Cohesion Strengths We have already defined and provided a summary of cohesion. Therefore, we can dive right into some specifics and strengths of this approach. I find simplicity to be one of the most apparent strengths of these solutions. While it provides a sort of "junk-drawer" approach in some cases, it also means we do not spend time deciding what goes where. When done properly, we can see this approach as having all of the important things you are working on sitting on your desk. Nothing is filed away. Thus, access is quick and easy. The Performance Aspect The lack of a need to "find things in your file cabinet" provides us efficiencies. In the code context, we have fewer abstraction layers to dig through for a value or function. Everything is readily available to the code, and permissions are a non-issue. This configuration provides for improved performance. Think of it as the best way to get something done is to do it yourself. Some developers see it as getting a task done quickly rather than creating classes to break the task into smaller pieces. Power In Simplicity There is power, as well as beauty, in simplicity. The product lines of Apple are a commercial example of this concept. We can remove a lot of time and decision points by limiting options. In the cohesion context, this comes from us knowing where a property or method belongs. We also spend less time worrying about what we should be able to see an attribute or method. When we take this to an extreme, we can see a near return to the old single file, functional approach to designing a software solution. We get to focus on functions over where they "live." When the solution is small, this all will make complete sense. Why complicate a solution needlessly?
15 minutes | 2 months ago
Cohesion and Coupling in Object-Oriented Design
Cohesion and coupling are options that are often discussed in object-oriented design circles. They are two ways to approach a solution. On the one hand, the solution can be a larger, self-contained approach. On the other hand, the solution can be a core that uses several "helpers" to achieve the objective(s). These are important options for us to consider as part of the overall OOD discussion. In this episode, we start with an overview of these common paths. Cohesion Defined There are many ways to think about Cohesion. However, I find it best to start with a common form of definition like the one found on Wikipedia. In computer programming, cohesion refers to the degree to which the elements inside a module belong together. In one sense, it is a measure of the strength of relationship between the methods and data of a class and some unifying purpose or concept served by that class. In another sense, it is a measure of the strength of relationship between the class's methods and data themselves. While we will not cover it at this point, note that there is an idea of a relationship among properties and methods in a class. This definition provides some foreshadowing in how we decide what method belongs where. Coupling Defined The ideas around coupling are much like cohesion. Therefore, let's start again with a simple definition like the one found on Wikipedia. In software engineering, coupling is the degree of interdependence between software modules; a measure of how closely connected two routines or modules are; the strength of the relationships between modules. The key concept here is found in the mention of interdependence between modules. It is a different focus from the inside of the module reference in our definition of cohesion. Cohesion and Coupling - Finding Their Place These two approaches are often seen as opposites. Thus you take one approach or the other in your design. However, that is not required. There can be some large objects with little coupling or large objects that are rather cohesive (in general), yet they have a substantial number of helper objects. These patterns of design do bring up the idea of consistency across a solution. On the other hand, they provide ways to handle different problems within a system in the best manner for that problem. The best way to look at these two options is to understand the strengths and weaknesses of each. Then we can find the best tool for the situation and expand our ways to solve each problem on its own.
14 minutes | 2 months ago
Property Design In An Object-Oriented System
One of the most prevalent topics of consideration in an object-oriented system is property design. We can keep them simple or create more classes. The first approach is simpler and faster to implement. However, we can use objects to provide hooks we extensibility and scaling out our features. There are trade-offs to be aware of, but we can use some guidelines to shorten the design process. Property Design Considerations For purposes of this episode, we will look at two paths available. We can use a simple type (native) or use a class to define a property. While we can also utilize collections as properties, those still boil down to one of these two paths. You will either have a collection of native values or object instances. A Software Upgrade A primary benefit of the object approach for a property is that it allows us to upgrade or "power-up" a class. A simple example of this is a greeting for a class. We can leave it as a string, or we can provide a greeting object. That object could start by displaying a greeting in English. When we want to upgrade the getting, we can add support for other languages or even an interactive greeting object. The Plugin Approach The above example can be viewed as a plugin. We can use this approach to provide a "quick and dirty" for the initial release and then improve it later. This pattern is excellent in an Agile environment in particular. We can build out functionality in one class. Then, we can inherit and extend that class later. The original features and functions are still available for instances built in the prior version. Yet, we can use the new approach for new instances. We get scalability along with stability. We have designed our solution for growth and flexibility. These are essential traits for an agile system.
17 minutes | 2 months ago
Class Relationships - When Has-A Shows a Relation
Our language can blur the distinctions of class relationships in designing our solution. The "has-a" determination is often confused by how the English language can translate those words. We also have to keep a context for assigning a class. Likewise, reality allows for data to be multiple classes in some cases, so we need to examine how those might be a hierarchy of some sort. Class Relationships - "Have" As A Pointer The confusing aspect of the English language is that we can "have" a relationship and a property. For example, you have an age, and you have a parent. The age is a property of being you while the parent is a relationship. This language blurring of properties can be confusing to those new to the idea of object-oriented design. Does It Stand On Its Own? The way to look at these different aspects with the same name is whether the target is itself an object. That is, can it stand on its own apart from the "owning" class. In our prior example, your age has no meaning apart from you. That age is not something you can give to someone else. Others have the same value for their age, but it is not the same age we are referring to. Your parent exists outside of you, and others can have that same parent. The Impact Of Change In the software world, it may help to look at what happens when the target property changes. Does it impact any other objects? If I change your name (a property you have), that only impacts you. It does not change anyone else's name. If your car changes, that impacts you and anyone else that links to that car. This is one of those data facets we see all the time. Do we copy a value, or can we have multiple pointers to it?
16 minutes | 2 months ago
Is A and Has A Concepts in Object-Oriented Design
The ideas of "is a" and "has a" are often discussed as part of object-oriented design. These concepts may seem simple and obvious. However, they can often be confused, and complex systems can blur the lines between them. We start a multi-part episode going over these concepts focused on how "is a" relationships work. Is A and Has A Defined These concepts boil down to simple grammar. For two objects, A and B, here are the options. A is a B if A has all of the traits of B. For example, a poodle is a dog because a poodle has the traits we equate to a dog. A dog is not a poodle, as some dogs have traits that are not shared with a poodle. A has a B when the entirety of B exists within A. A dog has a tail. The tail does not exist on its own away from the dog. More simply, a car has a color. In these relationships, multiple things can have the B part of the relationship. One Way Relationships For our purposes, these are one-way relationships. When A is a B, B is not an A. Likewise, when A has a B, B does not have an A. The color does not have a car; the tail does not have a dog. These definitions can seem to break when we replace names with instances. A father can have a son, and a son can have a father, but that is not a definition of contained properties. This example would be a relationship or pointers and not attributes that are contained. You have a height no matter what. You may not have a father if he passes away or something like that. As you can see already, grammar can confuse the situation, so we will continue to dig into these concepts and applying them.
16 minutes | 2 months ago
Granular Interfaces - How Much OO Is Practical?
The ideas of cohesion and coupling point us to paths that either place functionality in smaller or larger classes. We discuss granular interfaces in this episode as an introduction to those "right-sizing" discussions. Not all OO designs are created equal. Granular Interfaces - Bricks or Sand The best example I have come across in considering how to approach our design's granularity is comparing sand to bricks. We can construct a building by starting from sand, making blocks of concrete, and then moving on to the structure. However, it is easier when we can start from the blocks or bricks. The Lego Example We can similarly look at the children's toy Lego bricks. There are various sizes of bricks available. Therefore, we can build almost any structure we can imagine. The challenge is that we can build it out of combinations of larger bricks or work only with the one-by-one size bricks. The faster path will be to use larger bricks. The trade-off is that there are fewer customizations as we move to larger blocks. We see this in software design as well. The more granular our approach, the easier it is to customize. On the other hand, we do not want to a la carte our users into confusion. They should not have to hand-pick every single method or feature. Instead, we need to provide properly designed granular interfaces that group features that are best handled as a group. Grouping Methods While all assumptions will not hold in 100% of the cases, we can make assumptions to reduce our application's complexity. For example, we can assume that a need to save a class includes a need to load. Thus, we can take the CRUD functions and group them. In business instances, we will see a lot of the same natural groups. A customer contact feature will need to provide ways to create a customer, contact them, and track contact history. Therefore, a customer interface is naturally going to be easier to use if it includes all of those methods. These also help remind the developer (when using an interface) of functionality that should probably be implemented. At an application level, we can think of features like security, registration, help, and session management. These are not always needed. However, it is worth our while to intentionally determine whether our application can skip one or more of those.
15 minutes | 2 months ago
Inheritance - Polymorphism In A Hierarchical Manner
We switch gears in this episode and start to look at inheritance. This is a core feature of object-oriented design and the most recognizable attribute. Child classes are utilized through polymorphic support. Thus, we have a natural transition into this popular usage of object-oriented solutions. A Lowest Common Denominator We have discussed the idea of building on methods where possible. When we do, we are creating a lowest common denominator approach to functionality. This is what makes a system logical in its design. Whenever a user (developer) uses a method or attribute there are certain things they can expect that are included. For example, when I have "save" methods in classes, the user expects the class supports persistence. They also will expect a corresponding "load" is going to be available in that class. It is reasonable for the user to expect that a "save" followed by a "load" will result in the before/after instances being identical. Extending Through Inheritance We have the aforementioned set of expectations as developers. Those lead us to look at a hierarchy as something that grows as it goes. That means we should extend functionality as we inherit. We should avoid rewriting what the parent does. While we can change or even block behavior in child classes, that is rarely a good design. It is highly frustrating for a developer to have a method available to the parent that is no longer relative (rewritten or unavailable) further down the chain. Think of each step in the hierarchy as a way to build on the parent features. The base class supplies a foundation. The child classes add to that foundation without impacting what has been built in the prior layers. This approach will help your hierarchy help the developers that use it.
15 minutes | 2 months ago
Interfaces - An Object-Oriented Contract For Usage
This episode, we look at interfaces. These are not supported by all languages. However, when they are supported, they are very useful. The interface specification allows us to provide language-based constraints for our method signatures. It also is an excellent way to enforce consistancy. Interfaces As A Tool For Consistancy The most important result of using interfaces is that they enforce a consistant way to communicate with developers. Any class that uses one of these items will be forced to comply with the defined signatures. The language compiler or validations will let the developer know when they are not following the rules. While there might be empty methods allowed, the developer must specifically handle the method and return an empty result. Common Examples The typical methods and functions we have mentioened throughout this season are going to often end up in an interface. We see this in frameworks where there are numerous helper objects in the system. For example, there may be interfaces to save, print, load, export, or compare instances. These consistant interfaces allow us to treat a broad range of disparate objects the same. One such example would be sorting a list. We all can imagine sorting a list of names. On the other hand, how would we sort a list of cars? We can implement a compare interface in the car class. That can provide a process for comparing two cars and deciding an order to them. Once that has been implemented, we can display a list of cars and provide a sort option.
Terms of Service
Do Not Sell My Personal Information
© Stitcher 2021