DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

The Latest Software Design and Architecture Topics

article thumbnail
Fun With Modules
Tight coupling between modules is a bad idea, and the worst form of coupling is cyclic dependencies between modules. Fortunately, there are a few techniques we can use to break the cycles. They are Callback, Escalation, and Demotion, and I’m going to walk through some examples that show each of them in action. Then, once the dependencies are broken, we’ll look at two more techniques that allow us to invert and eliminate the relationship altogether. The code for each of the samples can be found in the edcie project on my Google code repository. Each example includes a build script and a simple test case. To execute them though, you’ll need GraphViz if you want to use JarAnalyzer. To invoke the build scripts without invoking JarAnalyzer, you can simply type: ant compile Keep in mind that each variation of the system has the exact same behavior! Modules Can Teach Us Plenty There are a number of amazing lessons that we can all learn when we use modules to help mold our software products and help us create things that customers will truly want to check out. However, we should experiment carefully with the different modules that are available to us before settling on any particular one. It may be the case that we need to sample several of them before we ultimately decide that one or the other is a better use of our time. Think about this carefully, and make sure you tweak your modules as necessary to get the best possible results out of them. You may discover that there are more than a few out there that make the most sense for you in the long run. The Example The example we’re going to use to drive the remainder of our discussion is incredibly simple. We’ve got a Customer and a Bill class that we’re going to bundle into two separate modules - cust.jar and bill.jar. There’s also a test case called PaymentTest that serves as a sample client to drive the interactions between the two classes. The test case is bundled into the billtest.jar module. The initial class diagram is seen on the right. Note the bi-directional relationship between the two classes. As we progress, we’ll add more classes and abstractions to the system to help increase the modularity. Additionally, we’re going to use JarAnalyzer to illustrate the relationships between the modules and also help us assess the quality of our design. The module structure is below, as generated by JarAnalyzer. You can see how to use JarAnalyzer in the build by reviewing the build file. Again, our goal is to break the cyclic dependency between cust.jar and bill.jar, and we’re going to look at three different ways to do this before moving on to examine different ways to massage acyclic module relationships. Initial Module Structure with Cyclic Dependencies Escalation The first technique we’re going to apply is called Escalation. With Escalation, we break the cyclic dependencies by escalating the cause of the dependency to a higher-level entity. Before we do that, we need to more fully understand why a cyclic dependency exists in this example. This reason follows: A Customer has a list of Bill instances. When the payment method on Bill is invoked, the Bill needs to determine if a discount should be applied. The discount is a product of the Customer the bill belongs to, not necessarily the Bill. Therefore, the Bill class calls for a method for Customers to determine the appropriate discount amount. Think of it this way…The Customer represents a payee and we negotiate a discount with each payee. The calculation of this discounted amount is encapsulated within the Customer. To break this dependency, we want to escalate the cause of the dependency up to a higher level class - the CustomerMediator. The mediator now encapsulates the calculation of the discount and passes that to the bill class. The best way to see this change is to look at the modified PaymentTest class. Now, I’ve modified the build script and have bundled the mediator into its own module, as shown below. If you dig a bit more deeply into the class structure, you’ll wonder why I didn’t just pass the discount amount from the Customer into Bill. Don’t worry about that. This example is slightly contrived because escalation isn’t the best way to solve this type of problem. The key takeaway is that we’ve escalated the dependency up to the mediator.jar bundle, breaking the cyclic dependency. Escalating the Cause of the Cyclic Dependency Demotion A slightly better way to solve this particular type of cyclic dependency (where we have a true composite relationship between Customer and Bill) is to use demotion. With demotion, we push the cause of the dependency to a lower-level module. Exactly the opposite of escalation. We do this by introducing a DiscountCalculator class that will be passed into the Bill class. Our modified PaymentTest class will create the calculator and pass it in for us. The Customer class will serve as the factory for the DiscountCalculator, since it’s the Customer that knows the discount that must be applied. The new class structure can be seen on the right. Now we’ll modify our build script to create an additional calc.jar bundle which will contain the DiscountCalculator class. Our resulting module structure is shown below. Demoting the Cause of the Cyclic Dependency Already you can see how this is a more natural solution than escalation for this particular type of cyclic dependency problem. What’s the key difference you might ask? With escalation, notice how I would be able to deploy the cust.jar and bill.jar modules independently. While demotion is a more natural solution in this situation, it also means that to deploy bill.jar or cust.jar, I must also deploy calc.jar. The right solution is always going to be contextual and the ideal solution is likely to shift throughout the development lifecycle. Callback Using a Callback is similar to the Observer pattern. With this approach, we’ll refactor our DiscountCalculator class to an interface, and then modify the Customer class to implement this interface. This new class structure can be seen on the right. As it happens in this specific situation, using a Callback represents a combination of demotion and our initial solution. We’ll go back to passing the Customer into the Bill, but will pass it in as a DiscountCalculator type. Whereas in the Demotion example we bundled the DiscountCalculator in a separate module, we’ll now just include it in our bill.jar module. Note that putting the DiscountCalculator in the cust.jar module would introduce the cyclic dependency we’re trying to get rid of. The new module structure, which resembles our original version minus the cyclic dependency, is shown below. Using a Callback to Eliminate the Cyclic Dependency Inverting Relationships Now we’re going to play around a bit with the module relationships. While Callback seems like the most logical solution, what if we wanted to use the cust.jar module without the bill.jar module? Callback, as it’s implemented, doesn’t allow us to do this. But with a bit of trickery, I can actually invert the relationship between the cust.jar and bill.jar modules. I start by refactoring the Bill class to an interface. Then, to avoid split packages (where classes in the same package are bundled into separate modules), I move the Bill class into the same package as the Customer class. The new class diagram is shown on the right, and the inverted module structure is shown below. Inverted Module Structure Eliminating Relationships Inverting the relationships allows us to deploy the cust.jar module independent of the bill.jar module. Again, it’s all about need. But I’d like to explore another option based on another important need - the ability to test modules independently. Before inverting the relationships, I am able to test the bill.jar module independently. After inverting the relationships, I can test the cust.jar module independently. But what if I want to test (or deploy) both modules independently? To do this, I need to completely eliminate the relationship altogether. As it turns out, because I’ve got a pretty flexible class structure after I inverted the relationships (lots of abstract coupling), I can do this by simply bundling the two interfaces - Bill and DiscountCalculator - into a separate module. No other coding changes are required. I start by moving them to a new package called base. Then, I modify my build script to bundle these two interfaces into a separate base.jar module, and we successfully eliminated the relationship between the bill.jar and cust.jar modules, as shown below. Eliminating Relationships Between Modules Wrapping Up We’ve come a long way from our original version of the system. Two modules with a cyclic relationship to a module structure where the original modules don’t have any relationship to each other. This means these modules can be tested and deployed independently. If you follow this blog, you know I’ve written plenty about the tradeoffs between flexibility and complexity, use and reuse, and many other architectural and design challenges. I hope this little exercise has helped drive some of those concepts home. As a final note, to explore some of these design decisions a bit more deeply, and to examine the tradeoffs a bit more objectively, I encourage you to run the builds for each of the projects and examine the dependencies.html file in the stats directory. To do this, you’ll need to make sure JarAnalyzer is executed, which requires GraphViz. As you’ll see when comparing the initial version to the final version, we made considerable progress in improving the quality of the design.
August 12, 2022
by Kirk Knoernschild
· 15,813 Views · 1 Like
article thumbnail
6 Myths About the Cloud That You Should Stop Believing
Learn more about the common misconceptions about using Cloud. Read more and discover how Cloud could also help your business grow.
August 12, 2022
by Pohan Lin
· 6,601 Views · 3 Likes
article thumbnail
Multi-Cloud Management: Tools, Challenges, and Best Practices
The process of tracking, securing, and optimizing multi-cloud deployment is called multi-cloud management.
August 12, 2022
by Mandar Navare
· 4,431 Views · 1 Like
article thumbnail
The Digital Finance Revolution, Open Banking, and APIs
Open banking and APIs are at the heart of the digital finance revolution. Monitoring APIs helps protect banks from issues including failures and slow-downs.
August 12, 2022
by Mehdi Daoudi
· 4,869 Views · 1 Like
article thumbnail
How to Govern Terraform States Using GitLab Enterprise
Most companies relying on Terraform for infrastructure management choose to do so with an orchestration tool. How can you govern Terraform states using GitLab Enterprise?
August 12, 2022
by Sefi Genis
· 5,019 Views · 1 Like
article thumbnail
5 Data Models for IoT
This blog presents five data modeling approaches to solve IoT data problems with Apache Cassandra.
August 11, 2022
by Artem Chebotko
· 7,990 Views · 1 Like
article thumbnail
4 Reasons MSPs Should Monitor Their GitHub Footprint
In this article, we will see why monitoring in real-time code-sharing platforms such as GitHub should be a top priority for any MSP.
August 11, 2022
by Thomas Segura
· 4,013 Views · 3 Likes
article thumbnail
How to Choose the Right Digital Experience Monitoring Solution
It would be best if you had digital experience monitoring for a transparent view of your IT infrastructure and how well it supports the needs of your customers.
August 11, 2022
by Mehdi Daoudi
· 3,587 Views · 1 Like
article thumbnail
Five Steps To Building a Tier 1 Service That Is Resilient to Outages
If Tier 1 services fail, it can mean disaster for a business. To ensure resiliency from outages, follow a proven five-step process.
August 11, 2022
by Pradeep Chinnam
· 6,851 Views · 4 Likes
article thumbnail
Conceptual Architecture: Conversational AI/NLP-Based Platform
Conceptual Architecture for Conversational Artificial Intelligence (AI) / Natural Language Processing (NLP) based Platform for Customer Support and Agent Services.
August 11, 2022
by Chandra Manohar
· 10,431 Views · 3 Likes
article thumbnail
Hide Your API Keys With an API Proxy Server
The main goal of this article/tutorial is to demonstrate how to conceal public API keys by building an API proxy server using NodeJS.
August 11, 2022
by Pantelis Theodosiou
· 5,614 Views · 2 Likes
article thumbnail
3 Must-Knows on Secure B2B Communication and AS2: The One-Stop Solution
Read an overview of the basics of cryptographically secured communications, with some real-world use cases to highlight the importance of this security measure.
Updated August 11, 2022
by Janaka Bandara
· 6,095 Views · 3 Likes
article thumbnail
Key Highlights from the New NIST SSDF
In this article, we’ll be going over the 1.1 revision of the Secure Software Development Framework that was published earlier this year.
August 11, 2022
by Shimon Brathwaite
· 5,244 Views · 1 Like
article thumbnail
5 Steps to Rethink High Severity to Save Developer Productivity
Security remediations occupy a growing portion of developers’ time. We can reframe how we define a "critical" vulnerability and ask: Is it attackable?
August 11, 2022
by Arun Balakrishnan
· 4,962 Views · 1 Like
article thumbnail
Azure Databricks Automated Testing Using Great Expectations and C#
This article will show you how to use the Great Expectations library to test data migration and how to automate your tests in Azure Databricks using C# and NUnit.
August 11, 2022
by Mohammed Basil
· 11,452 Views · 3 Likes
article thumbnail
SRE: From Theory to Practice: What’s Difficult About Tech Debt?
How do you get ahead of tech debt before it piles up? And how do you deal with tech debt you already have, without sacrificing velocity on new projects?
August 11, 2022
by Emily Arnott
· 6,050 Views · 1 Like
article thumbnail
10 Best Infrastructure-as-Code Tools for Automating Deployments in 2022
With the rapidly changing technology landscape, the traditional approaches to infrastructure are hampering businesses to adapt, innovate, and thrive optimally. Now, Infrastructure as Code (IaC) tools have emerged as the key to navigating this challenge.
August 10, 2022
by Vishnu Vasudevan
· 7,503 Views · 2 Likes
article thumbnail
Back to Basics: Accessing Kubernetes Pods
Kubernetes is a colossal beast. You need to understand many concepts before it starts being useful. Here, learn several ways to access pods outside the cluster.
August 10, 2022
by Nicolas Fränkel
· 4,794 Views · 4 Likes
article thumbnail
Decorator Pattern to Solve Integration Scenarios in Existing Systems
This article provides an example of how the Java Decorator Pattern helps create designs from scratch and fix integration problems in existing systems.
August 10, 2022
by Mario Casari
· 7,693 Views · 6 Likes
article thumbnail
8 Cross-Platform Tools for Mobile App Development
Cross-platform mobile app development helps to create mobile apps that can be deployed or published on multiple platforms using a single codebase.
August 10, 2022
by Hiren Dhaduk
· 7,698 Views · 1 Like
  • Previous
  • ...
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • ...
  • Next
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook
×