SOEN-6461 Software Design Methodologies

Hello, if you have any need, please feel free to consult us, this is my wechat: wx91due

SOEN-6461

Software Design Methodologies

Fall 2024

Initial Setup:

1. We can clone the project repository URL provided by the professor as part of the project deliverable: https://github.com/ptidejteam/ptidej-Ptidej

2. Our team has created a fork of the professor’s repository, as we might be updating the codebase for our upcoming deliverables. Our team fork could be accessed at the below mentioned url.
Team Fork: https://github.com/DarCyStorm/ptidej-Ptidej-groupA

3. To clone the created fork, use the following command:

git clone https://github.com/DarCyStorm/ptidej-Ptidej-groupA

4. Install Maven using homebrew or any package manager in your system if it is not already installed using the following commands:

Update homebrew: brew update

Install gradle: brew install maven

Verify Installation: mvn –version

5. Now to build the project, our team referred to the README section of the repository and successfully executed the following commands:

mvn dependency:purge-local-repository -DactTransitively=false -DreResolve=false

mvn clean

mvn validate

mvn install

6. A successful project build with passing all test cases should show the following output as shown in the terminal:

Fig: Successful project build

7. We attempted to run the application using the command mentioned in the README file. However, it takes a considerable amount of time for the application to start, as noted in the TODO section of project’s GitHub repository.

java -jar "DeMIMA UI Viewer Standalone Swing/target/demima-ui-viewer-swing-1.0.0-jar-with-dependencies.jar"

8. To run the application, we executed the following file located in the DeMIMA UI Viewer Standalone Swing module at the path /src/main/java/ptidej/viewer/ProjectViewer and obtained the following project GUI screen:

Fig: Successful execution of application

What do we run, how and why?

1. Once our application was up and running, we interacted with the user interface provided by the application and tried various options just like a new user who does not have any knowledge about the system will interact.

2. We figured out that the File menu is providing different options to actually load class files, jar file, java source files, cpp files and much more.

3. So we, as users, went through the available projects in our system that we might have built or studied in some other courses at our university.

4. One of our teammates was actually learning design patterns from a course at Udemy and he informed us that he has the practice java files available with him, so we decided to run those files in the project.

5. Below is the snapshot of the project structure of the project that our team executed in the application.

6. We build the jar of the project using maven and running the command: mvn clean install

7. Project’s JAR file was also created successfully in the target directory of the project along with the .class files once the build was successful.

8. By following the above steps, we were able to get a set of class files and a jar file ready to be loaded into the PTIDEJ project of our course.

Fig: Project to be run in PTIDEJ application

Fig: Successful build and generation of jar and class files

Running a java project in PTIDEJ:

1. Click on File -> Add -> Java Class Files option on the project’s GUI as shown and give a name to the imported project in the application:

Fig: Importing .class file in project

2. Load the class files of the project that we are targeting to analyse:

Fig: Importing generated class files in application

3. A successful import of classes should show the following output where we can actually analyse the hierarchies of classes and interfaces. Which class is implementing which interface.

4. The project provides a very clean visualisation of the classes getting analysed.

5. We also tried to view the statistics generated by the application using the Show Model Statistics option which helps to obtain count of classes, interfaces, relationships including associations and aggregation, number of fields and many more as shown below:

Fig: Model Statistics result on the imported classes

6. We also tried to List Model Entities and verified if the entities are the same as present in the actual project structure as shown:

Fig: List of Entities getting analysed in the project

7. Then we can go to the Code Smells section and it can even help us to identify the smells by highlighting the selected type of Smell.

8. We selected the AbstractClass option as shown in the below figure and all the abstract classes are highlighted in the window showing the hierarchy of classes and interfaces.

Fig: AbstractClass Code Smells analysis Help

9. Note: Our team also tried to run all the same operations by importing the .jar file in the application, however, similar result was not obtained and none of the classes were getting imported on trying to import jar file as shown below:

Fig: Unexpected results on trying to load the jar file in application

Explanation of why we did this

First of all, we executed these operations on a project with which we are very familiar and it will be easier for us to validate if the results are accurate or not. The tool in turn helped us to visualise class structures, hierarchies, and code smells based on our current exploration of the tool. Our main goal was to get our hands dirty with the tool and have a deeper understanding of the code flow, design patterns getting used and how the tool can help us to improve real-world projects.

By doing the above mentioned operations we became a bit familiar with the application which will be very critical for the upcoming deliverables of our project. While doing this we also figured out some of the options in the tool which might be improved in the upcoming deliverables which will improve the usefulness of the application.

1. Purposes of Packages:

Package1: padl.analysis

To address the purpose of this package at first we generate an automated package diagram through Intellij Idea built-in diagram maker. In the given diagram we enabled the diagram to show fields, constructors, methods, properties, inner classes and dependencies. Diagram is available in [link] and you can review it by uploading it in draw.io. Also for a better understanding of dependencies(because there are too many details in the above diagram) we also generate a diagram for only dependencies in the package as shown below.

Fig: Dependencies of package padl.analysis

After analysing relationships in the package we figured out that we saw classes like AACRelationshipsAnalysis That specialise in different types of architectural or design-level relationships. These classes seem to be focusing on Interpreting dependencies between various entities like classes, interfaces and packages within a software model. The purpose is to provide a systematic evaluation of the interdependencies and coupling between software components. For example in the AACRelationshipsAnalysis as you can see in the below figure AACRelationshipsAnalysis.invoke(IAbstractModel) method might take a model like UML as input and identify or interpret relationships like associations, dependencies, or inheritance. So the potential use case might be analysing how tightly coupled certain modules are within a design to identify areas of improvement or refactoring opportunities.

Fig: AACRelationshipsAnalaysis class diagram

This package also has classes like AACBuilder and AACRemover which will handle the creation and modification of models. This involves adding, removing, or updating components in an abstract model structure. These classes aim to support the dynamic construction or manipulation of design models. For example, AACRemover.visit(IField) visits a field entity within a model to evaluate whether it should be removed based on some criteria. AACBuilder appears to be involved in constructing models, and opening different model elements like classes (IClass), methods (IMethod), and packages (IPackage). So, it may be a part of a model-building workflow.

Fig: AACBuilder class diagram

AACRemover has a functionality as the counterpart to AACBuilder, where it likely traverses the model to remove or reset elements based on analysis or user-defined rules.

Fig: AACBuilder class diagram

Also, there is a class like PlantUMLGenerator, which helps to integrate with tools like PlantUML for visualising the results of analyses To create visual representations of the analysed models, which can be used for better understanding and further documentation. So, it will help generate class diagrams that show the analysed relationships or visualise the structural changes.

Fig: PlantUMLGenerator class diagram

In conclusion, we understand this package can be used to analyse a software architect to evaluate the relationships between various components, identifying areas with high coupling or complex dependencies. Also, you can leverage the AACBuilder and AACRemover classes to automate the refactoring of design models. For example, they could programmatically remove unnecessary methods. Also as we mentioned by using the PlantUMLGenerator you can generate visual representations of software design.

So, the padl.analysis package serves as a comprehensive entity for analysing the design and architecture of software models, managing those models through dynamic building or removing of components and visualising the outcomes of those analyses and management through generated diagrams.

Package2: ptidej.ui.analysis

For this package we conduct the same path as Package1: padl.analysis so first we generate this package diagram with details of fields, constructors, methods, properties, inner classes and dependencies which is shown in figure below.

Fig: Package ptidej.ui.analysis diagram

After analysing the above figure we understand that we have IUIAnalysis which is an interface class, as indicated by the <> in the diagram. Also, we have UIAnalysesRepository which is a repository class that likely manages and stores various UI analysis implementations. Also, we have a package named ptidej.ui.analysis.repository which we will go into detail in continuation.

In the IUIAnalysis interface we have a couple of methods. createBuilder(IPrimitiveFactory) method seems to create and return a builder object. invoke(IAbstractModel) method likely performs an operation or analysis on a given abstract model and returns an analysed or modified version of it. Also in UIAnalysesRepository class, we have a toString() which overrides the toString method, possibly for displaying repository information. In the attributes part we have instance which is a singleton instance of the repository. Also, UIAnalyses is an array of IUIAnalysis instances, indicating that this repository manages multiple analysis objects.

So, based on the classes and methods in the ptidej.ui.analysis package appears to serve the following purposes the IUIAnalysis interface sets the structure for all UI analysis classes. Any concrete implementation of this interface would need to define how to create a builder object and how to invoke analysis on a model because it is an interface. It ensures that different types of UI analyses conform to a consistent structure, making it easier to manage and invoke them programmatically (Adding a level of abstraction).

The UIAnalysesRepository class can be used to find or iterate over available analyses and invoke them. It also suggests a singleton to ensure only one repository instance exists.

Now as we mentioned we go in deltail of ptidej.ui.analysis.repository. In order to do so we extract the diagram for this package.

Fig: Package ptidej.ui.analysis.repository diagram

The sub-package ptidej.ui.analysis.repository is for an analysis framework supporting differences between models in software. So, this package aims to analyse where changes to software architecture or design need to be tracked and visualised.

ModelComparator class is the core component for comparing two or more software models. Its purpose is to detect changes or similarities in version control and model comparison, possible refractories, and change impact. So, implementing a generic comparison mechanism enables other classes in the package to focus on specific types of differences.

DifferenceHighlighterFromClasses class aims to highlight differences between classes within the models like differences before and after changes like modifications in attributes. So, it provides a view of how classes evolve. So, this class can analyse structural integrity and ensure that changes in one class do not violate others in a bad way or any unwanted side effect (Blocking ripple effect).

DifferenceHighlighterFromMethods class is similar to DifferenceHighlighterFromClasses, this class focuses on detecting and highlighting differences at the method level like changes in method signatures. It enables analysis of behaviour changes and shows how modifications in methods impact class behaviour and interactions.

In conclusion, the ptidej.ui.analysis.repository sub-package has been designed for model comparison and showing of their differences. It can help developers keep up with software evolution by providing them with ways of comparing different versions and understanding modifications. As classes in this package programmed they can help with visualisation of changes and impacts, assists in identifying changes in order to make it easier to suggest refactoring or revert problematic modifications. So, this sub-package is about maintaining the integrity as they evolve, ensuring that changes do not compromise the design and functionality of the system.

How client developers can use the method invoke(IAbstractModel) in class AACRelationshipsAnalysis present in package padl.analysis.repository

The following sequence diagram [link] illustrates the interaction between the client and the AACRelationshipsAnalysis class:

Fig: Sequence Diagram illustrating client’s usage of AACRelationshipsAnalysis

Steps for Client Developer to use AACRelationshipsAnalysis

● First of all, the developer creates or obtains an ICodeLevelModel using the Factory class.

● The client creates an instance of AACRelationshipsAnalysis and optionally also specifies whether to remove old relationships or not by passing a boolean parameter in the constructor.

● The client calls the invoke() method and passes the ICodeLevelModel instance.

● The invoke() method checks if the passed model is an instance of ICodeLevelModel.

● If it is an instance of ICodeLevelModel, the model is then cloned and then it is wrapped in an IdiomLevelModelAdapter which is then returned as an instance of IAbstractModel.

● The client then casts it to IIdiomLevelModel and then uses it as shown below:

Fig: Client Developer’s usage of AACRelationshipsAnalysis

Pros and Cons of using AACRelationshipsAnalysis

Pros

● Modularity: The invoke() method is designed to handle all the complex logic for analysing relationships in one place which is increasing the modularity of code.

● Flexibility: The client can choose whether to remove old relationships by passing a boolean flag during the creation of an instance in the constructor of AACRelationshipsAnalysis.

● Error Handling: The method throws meaningful exceptions (UnsupportedSourceModelException) when invalid model types are passed as an argument.

Cons

● Single Type Support: The method only supports ICodeLevelModel instances and will throw exceptions for other implementations of  IAbstractModel which is not the expectation of the invoker.

● Performance Overhead: Cloning method is being called in the invoke method which incur a lot of performance costs, especially since the codebase of the project is very large.

● Careful Casting: Client needs to cast the returned instance into the correct type carefully and might face ClassCastException if it is not done in the correct way.

2. Identification and Reporting

1. Distinguishing typing from code reuse

Typing Example from project

In the padl.kernel.impl.CodeLevelModel class, we observe the use of typing through the implementation of the ICodeLevelModel interface. This interface defines the expected behaviour of a code-level model in PADL, but it does not enforce any specific implementation; rather it is only providing the contract which all the classes implementing the ICodeLevelModel interface must satisfy.

Fig: CodeLevelModel a concrete type of ICodeLevelModel

Fig: Contract defined by ICodeLevelModel

Explanation of Typing: Here, CodeLevelModel provides its own implementation of the methods defined by the ICodeLevelModel interface. This allows for flexibility, as different implementations of ICodeLevelModel can help in providing different ways of creating or managing code-level models, while making sure that they follow the same interface (contract).

Code Reuse Example From Project

The class CodeLevelModel class also extends the  class AbstractLevelModel, which in turn extends another abstract class AbstractModel. This abstract class (AbstractModel) provides reusable functionality that the CodeLevelModel inherits which includes public methods like addConstituent(), addModelListener(), addModelListeners() which are available in CodeLevelModel class and are getting reused.

Fig: CodeLevelModel extends AbstractLevelModel, which further extends AbstractModel

Fig: Reusable methods available in Abstract Model

Explanation: As we have shown above in the snapshots that the CodeLevelModel class reuses functionality that has already been defined in its parent classes, AbstractLevelModel and AbstractModel. This allows CodeLevelModel to focus on its own specific behaviour while inheriting common functionality from these concrete and abstract classes respectively.

Pros and Cons of Typing and Code Reuse decisions

Pros of Typing

● Easy to Change: The ICodeLevelModel interface allows developers to write multiple implementations which in turn enables easy change of concrete implementation and it is very easy to extend the code in future.

● Loose Coupling: The system is decoupled from specific implementations which means that it will be  easier to make changes without making any impact on dependent code.

Cons of Typing

● No Code Reuse: As we know that interfaces do not provide any implementation which means that each class must define its own behaviour and there are chances that we will have to do potentially code duplication.

● Complexity: Since there could be multiple implementations of the same interface which means that it will be difficult to do debugging since developers will have to understand which implementation is going to be invoked.

Pros of Code Reuse

● Less Duplication: As our class is Inheriting from AbstractLevelModel and AbstractModel which allows our concrete class CodeLevelModel to reuse existing functionality which is already present in the inherited classes.

● Easy to make changes: If developers make a change in the base class then that change is very quickly propagated automatically to all subclasses which  simplifies the updates.

Cons of Code Reuse

● Tight Coupling: As we know that by using inheritance, child classes become strongly coupled to parent classes which means that changes to the base class can have unintended side effects on the child classes.

● Long Inheritance trees: If there is a multilevel inheritance for code reuse then this long inheritance chain can make it harder to trace the behaviour available in the child classes which increases the complexity of the system.

2. Implementing duck typing

Given that Java is a statically-typed language, implementing Duck-Typing is not as straightforward as it is for dynamically-typed languages, such as Python. It is possible however to mimic the concept of duck-typing via using interfaces to allow different classes to be used interchangeably if they implement the same methods.



Duck-Typing Example from project


In the PADL/src/main/java/padl/kernel/impl/Operation.java class, the use of iterators that can work with different types of constituents can be noted, which allows for flexible handling of various object types. The method getIteratorOnConstituents takes a Class object as a parameter.

It returns an Iterator that can iterate over constituents of the specified type,

demonstrating a flexible and type-safe way to work with different object types based on their capabilities rather than their specific class.

Duck Typing Simulation

● Type Parameterization: The method accepts a java.lang.Class parameter, allowing for runtime specification of the desired constituent type.

● Runtime Type Checking: The use of a Class object as a parameter suggests that type checking is performed at runtime, similar to duck typing in dynamically-typed languages.

● Polymorphic Behaviour: The method can return an iterator for different types of constituents, demonstrating polymorphism - a key aspect of duck typing.

Pros of Duck-Typing Simulation

● Flexibility: Allows for handling different constituent types without changing the method signature.

● Type Safety: Maintains Java's type safety while providing runtime flexibility.

● Code Reusability: A single method can handle multiple constituent types, reducing code duplication.

Cons of Duck-Typing Simulation

● Not True Duck Typing: Still relies on explicit type checking rather than just method availability.

● Runtime Overhead: Type checking at runtime can introduce some performance overhead.

3. Adding one level of indirection

发表评论

电子邮件地址不会被公开。 必填项已用*标注