dimanche 3 octobre 2021

Is my library following the SOLID principles and a good design? [closed]

Question & Problem Description

Hello Everyone, this is my first Stack Overflow Question, I tried to be as clear as possible.

I'm trying to develop a kind of Library that helps me when I need to build a Command Line Application.

Since I started its development I have had problems thinking with the use of a good architecture. At the moment, my library has a single functionality and is printing a kind of "responsive" menu in the command line screen, it is more simple that it sounds.

To solve this scenario while trying to follow the SOLID principles I created a class called UIMenu that has the responsibility of giving access the object properties and methods. Then I created another class named UIMenuPrinter that has the responsibility of printing a UIMenu with a specified format.

All is working correctly, but even with this division of responsibility I found a little bit tedious to create a UIMenu object and then creating a UIMenuPrinter object to pass as a parameter the previous UIMenu object. So I would like to know what do you think, is this solution a good design or maybe I should improve in something. I was also thinking about implementing the UIMenuPrinter as a Inner Class, so I don't need to instantiate another object.

I was looking for ideas through Google OC, but all that I found was in general talking about SOLID Principles and good practices.

If you have suggestions I'm all ears!

Solution Code

UIMenu Class

using System.Collections.Generic;

namespace CLILibrary
{
    public class UIMenu : UIControl
    {
        public string Title { get; set; }
        public List<string> MenuRows { get; }

        public UIMenu() => MenuRows = new List<string>();

        public void AppendLine(string line)
        {
            MenuRows.Add(line);
        }
    }
}

UIMenuPrinter Class


using System;
using System.Collections.Generic;
using System.Text;
using CLITools;

namespace CLILibrary.UIComponentPrinters
{
    public class UIMenuPrinter : UIComponentPrinter
    {
        public UIMenu UiMenu { get; set; }
        
        public char FrameDecorationChar { get; set; } = '=';
        public char SeparationChar { get; set; } = ' ';
        public char RowDecorationChar { get; set; } = '|';

        public UIMenuPrinter (UIMenu uiMenu)
        {
            UiMenu = uiMenu;
        }

        public override void Print()
        {
            var values = new Stack<string>();
            _StackPropertiesLoader(ref values);
            var stringMeter = new StringMeter(values);
            var largestStringSize = stringMeter.MeasureLargestString();
            var largestString = (largestStringSize >= 40) ? largestStringSize : 40;
            
            StringBuilder formatBuilder = new StringBuilder();

            var frameDecorationLine = StringRepeater.Repeat(FrameDecorationChar.ToString(), (int) largestString);
            var spacesLine = StringRepeater.Repeat(SeparationChar.ToString(), (int) largestString);

            formatBuilder.Append(SeparationChar).Append(frameDecorationLine).Append(SeparationChar);
            
            formatBuilder.AppendLine();

            formatBuilder.Append(RowDecorationChar).Append(spacesLine).Append(RowDecorationChar).AppendLine();
            formatBuilder.Append(RowDecorationChar).Append(_FillRow(UiMenu.Title, (int) largestString)).Append(RowDecorationChar).AppendLine();
            formatBuilder.Append(RowDecorationChar).Append(spacesLine).Append(RowDecorationChar).AppendLine();
            
            formatBuilder.Append(SeparationChar).Append(frameDecorationLine).Append(SeparationChar).AppendLine();
            
            UiMenu.MenuRows.ForEach(Row =>
            {
                formatBuilder.Append(RowDecorationChar).Append(_FillRow(Row, (int) largestString)).Append(RowDecorationChar).AppendLine();
            });
            
            formatBuilder.Append(SeparationChar).Append(frameDecorationLine).Append(SeparationChar).AppendLine();
            
            Console.WriteLine(formatBuilder.ToString());
        }

        private string _FillRow(string row, int limitLength)
        {
            return $"{row}{StringRepeater.Repeat(SeparationChar.ToString(), limitLength - row.Length)}";
        }

        private void _StackPropertiesLoader(ref Stack<string> propertiesStack)
        {
            foreach (string Row in UiMenu.MenuRows)
            {
                propertiesStack.Push(Row);
            }

            propertiesStack.Push(UiMenu.Title);
        }
    }
}

SandBox Console Application for Basic Testing Code

using System;
using CLILibrary;
using CLILibrary.UIComponentPrinters;
using CLITools;

namespace SandBox
{
    internal class Program
    {
        public static void Main(string[] args)
        {

            var mainMenu = new UIMenu
            {
                Title = "Do you like to drink coffee?"
            };

            mainMenu.AppendLine("[1] Yes");
            mainMenu.AppendLine("[2] No");

            var uiMenuPrinter = new UIMenuPrinter(mainMenu);
            uiMenuPrinter.FrameDecorationChar = '*';


            uiMenuPrinter.Print();

            Console.ReadLine();
        }
    }
}

Extra Details

  • Also I have one class that is an Abstract Class named UIControl that is the Parent of all the specific UI Components like UIMenu is.

  • Also with the UIPrinter object I have a Parent of all the Component Printers, its name is UIComponentPrinter.

UML Diagram

UML Class Diagram

Aucun commentaire:

Enregistrer un commentaire