Jump to content
uniGUI Discussion Forums
elGringo

MVC pattern in UniGUI vs traditional coding to form

Recommended Posts

Hi, dear all. Learning ASP MVC. Question is in implementing MVC pattern in UniGUI framework.

I want to understand,starting this discussion

  -is there any sense of implementing of this pattern in UNIGUI, cause main plus of MVC is better structure of app and better testing

   -what are concrete ways of implementations,  maybe simple examples...

  -is someone tried? Any effort? Any experience? Share please your thoughts and practices you faced with.

 

As for my thoughts

  

  Models code could be in MainModule

  Views are forms

   Controllers are separate units...

 

On the other hand - traditional Delphi style codeing to form when everything is coded in form and that is bad practice, because it is good on the start, good as RAD approach, but after dozens of units it become to be a mess, don't you reckon like this?

 

That is the starting point...

Share this post


Link to post
Share on other sites

ok, it seems to me i am the first who appeal to this theme ) I'm going to do my next project in UniGUI in MVC pattern, i will share my experience here.

Share this post


Link to post
Share on other sites

Have no experiences in MVC/UniGui but would be interested too..

Share this post


Link to post
Share on other sites

Hi guys,

 

The theory is beautiful, but practical results are more important.

 

The MVC pattern intends to achieve a clear separation between the user interface and the business logic.

This goal becomes paramount when you need to share the business logic when you target several platforms for your user interface.

In uniGUI you already have two targets: desktop and mobile (touch).

 

Delphi already provides tools for achieving that separation without having to add unnecessary code.

Basically, any Delphi form can be bound to objects in a data module using event handlers and actions.

Just by using these tools, you won't need anything else for achieving the separation.

 

The Small Hybrid App in Demos\Touch uses this approach.

 

I hope this helps...

Share this post


Link to post
Share on other sites

Yes! That is the thoughts i waited for ! Delphi has its unique architecture.

Form itself has dfm and pas parts that are bounded, the engeneers of Delphi tried to give us more speed in developing soft! 

So on start we have already structure we can use. Actions and events (procedures and functions of object) are those tools to connect everything to everything.

Other question - is how convenient is it? How easy to read and understand this code to new person of project?

 

Experiment shows all! Lets write simple app that just will concat 2 Uniedits here in different patterns and together understand which approach is better!

 

Ok, having

    eLeft: TUniEdit;
    eRight: TUniEdit;
    lResult: TUniLabel;
    bConcat: TUniBitBtn;

1 st approach is Delphi approach - concat directly using form vars

procedure TMainForm.bConcatClick(Sender: TObject);
begin
lResult.Caption:=eLeft.text+eRight.Text;
end;

2d approach is MVC

 

 creating models with sync to main

unit uModel;

interface

uses
  SysUtils, Classes;

type
  TModel = class(TDataModule)
  private
    FLeft: string;
    FRight: string;
    FResult: string;
    procedure SetLeft(const Value: string);
    procedure SetRight(const Value: string);
    procedure SetResult(const Value: string);
    { Private declarations }

  public
    { Public declarations }
    property Left:string read FLeft write SetLeft;
    property Right:string read FRight write SetRight;
    property Result:string read FResult write SetResult;
  end;

function Model: TModel;

implementation

{$R *.dfm}

uses
  UniGUIVars, uniGUIMainModule, MainModule,Main;

function Model: TModel;
begin
  Result := TModel(UniMainModule.GetModuleInstance(TModel));
end;

{ TModel }

procedure TModel.SetLeft(const Value: string);
begin
  if (Value<>FLeft) then
  begin
  FLeft := Value;
  MainForm.eLeft.Text:=Value;
  end;
end;

procedure TModel.SetResult(const Value: string);
begin
  if (Value<>FLeft) then
  begin
  FResult := Value;
  MainForm.lResult.Caption:=Value;
  end;
end;

procedure TModel.SetRight(const Value: string);
begin
if (Value<>FRight) then
  begin
  FRight := Value;
  MainForm.eRight.Text:=Value;
  end;
end;

initialization
  RegisterModuleClass(TModel);

end.

Model is created in UniMainModule

procedure TUniMainModule.UniGUIMainModuleCreate(Sender: TObject);
begin
FModel:=TModel.Create(UniApplication);
end;

Sync View to model

procedure TMainForm.eLeftChange(Sender: TObject);
begin
UniMainModule.Model.Left:=eLeft.Text;
end;

procedure TMainForm.eRightChange(Sender: TObject);
begin
UniMainModule.Model.Right:=eRight.Text;
end;

Controller method

procedure TController.Concat;
begin
with UniMainModule do begin
Model.Result:=Model.Left+Model.Right;
end;

full Controller

unit uController;

interface

uses
  SysUtils, Classes;

type
  TController = class(TDataModule)
  private
    { Private declarations }
  public
    { Public declarations }
    procedure Concat();
  end;

function Controller: TController;

implementation

{$R *.dfm}

uses
  UniGUIVars, uniGUIMainModule, MainModule;

function Controller: TController;
begin
  Result := TController(UniMainModule.GetModuleInstance(TController));
end;

{ TController }

procedure TController.Concat;
begin
with UniMainModule do begin
Model.Result:=Model.Left+Model.Right;
end;

end;

initialization
  RegisterModuleClass(TController);

end.

instance of Controller called directly as UniGUI allows it

 

 

use of MVC approach

procedure TMainForm.bConcatClick(Sender: TObject);
begin
UniMainModule.Controller.Concat();
end;

My conclusions

 -mvc is more difficult in realization

 -delphi already has its unique structure and there is unique Delphi pattern, no needed to import MVC, but possible

 -on that small example I easily used already done infrastructure of Delphi components and to concat i just needed to write one string!

 -as for mvc approach i needed to do a lot of additional work, create new structure in model, sync it to view and view to it, and this sync is not full...

 

in ASP MVC for example not other structure then MVC, so if not use MVC unclear how to organize coding, so in ASP MVC it is necessary to use mvc Approach, in Delphi we can choose, and it is advantage...

 

Maybe in big projects mvc is better, but small test shows lots of additional work...

Share this post


Link to post
Share on other sites

Besides!

Firstly I wanted to show code of simple calc like this below in 2 patterns, but I don't even want to think how complicated it to transfer in MVC pattern... 

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, uniGUITypes, uniGUIAbstractClasses, uniGUIClasses, uniGUIRegClasses,
  uniGUIForm, uniEdit, uniGUIBaseClasses, uniButton, uniBitBtn, uniLabel;

type
  TMainForm = class(TUniForm)
    b8: TUniBitBtn;
    b9: TUniBitBtn;
    b4: TUniBitBtn;
    b5: TUniBitBtn;
    b6: TUniBitBtn;
    bOne: TUniBitBtn;
    b2: TUniBitBtn;
    b3: TUniBitBtn;
    eResult: TUniEdit;
    UniBitBtn10: TUniBitBtn;
    bEqual: TUniBitBtn;
    bDivide: TUniBitBtn;
    bMultiply: TUniBitBtn;
    bMinus: TUniBitBtn;
    bPlus: TUniBitBtn;
    bCE: TUniBitBtn;
    b7: TUniBitBtn;
    unilabel: TUniLabel;
    procedure UniFormCreate(Sender: TObject);
    procedure digitClick(Sender: TObject);
    procedure signClick(Sender: TObject);
    procedure bEqualClick(Sender: TObject);
    procedure bCEClick(Sender: TObject);
  private
    { Private declarations }
    faIsEmpty: Boolean;
    fbIsEmpty: Boolean;
    fcIsEmpty: Boolean;
    fdIsEmpty: Boolean;
    fIsSignClickedBefore: Boolean;
    fIsDigitClicked: Boolean;
    fa, fb, fc: real;
    fd: char;
    foperators: set of Char;
    procedure nullAll;
  public
    { Public declarations }
  end;

function MainForm: TMainForm;

implementation

{$R *.dfm}

uses
  uniGUIVars, MainModule, uniGUIApplication, System.Generics.Collections;

function MainForm: TMainForm;
begin
  Result := TMainForm(UniMainModule.GetFormInstance(TMainForm));
end;

procedure TMainForm.bCEClick(Sender: TObject);
begin
  nullAll();
end;

procedure TMainForm.bEqualClick(Sender: TObject);
begin
  case fd of
    '-':
      fc := fa - fb;
    '+':
      fc := fa + fb;
    '*':
      fc := fa * fb;
    '/':
      fc := fa / fb;
  end;
  fa := fc;
  fbIsEmpty := True;
  eResult.Text := FloatToStr(fc);
end;

procedure TMainForm.nullAll;
begin
  fa := 0.00;
  fb := 0.00;
  fc := 0.00;
  unilabel.Caption := '';
  faIsEmpty := true;
  fbIsEmpty := true;
  fcIsEmpty := true;
  fdIsEmpty := true;
  foperators := ['+', '-', '*', '/'];
  unilabel.Caption := '';
  eResult.Text := '';
end;

procedure TMainForm.digitClick(Sender: TObject);
begin
  fIsSignClickedBefore := false;
  fIsDigitClicked := True;
  if faIsEmpty then
  begin
    fa := strToFloat((Sender as TUniBitbtn).Caption);
    faIsEmpty := false;
  end
  else
  begin
    fb := strToFloat((Sender as TUniBitbtn).Caption);
    fbIsEmpty := False;
  end;
  unilabel.Caption := unilabel.Caption + (Sender as TUniBitbtn).Caption;
end;

procedure TMainForm.signClick(Sender: TObject);
var
  s: string;
  lastSymbol:char;
begin
  fdIsEmpty := false;
  if fIsSignClickedBefore then
  begin
    s := unilabel.Caption;
    lastSymbol:=s[Length(s)];
    if ((s[Length(s)] in foperators) and ((lastSymbol <> (Sender as TUniBitbtn).Caption))) then
    begin
      delete(s, length(s), 1);
      fdIsEmpty := true;
      unilabel.Caption := s;
      //fIsSignClickedBefore := false;
    end
    else if (s[Length(s)] = (Sender as TUniBitbtn).Caption) then
      exit;
  end;

  unilabel.Caption := unilabel.Caption + (Sender as TUniBitbtn).Caption;
  s := (Sender as TUniBitBtn).Caption;
  fd := s[Low(s)];
  if fdIsEmpty then
    exit;

  if not fIsDigitClicked and fIsSignClickedBefore then
    exit;

  fIsSignClickedBefore := true;

  if (not faIsEmpty) and (not fbIsEmpty) then
  begin
    case fd of
      '-':
        fc := fa - fb;
      '+':
        fc := fa + fb;
      '*':
        fc := fa * fb;
      '/':
        fc := fa / fb;
    end;
    fa := fc;
    eResult.Text := FloatToStr(fc);
    fIsDigitClicked := false;
  end;
end;

procedure TMainForm.UniFormCreate(Sender: TObject);
begin
  nullAll();
end;


initialization
  RegisterAppFormClass(TMainForm);

end.

Share this post


Link to post
Share on other sites

MVC paradigm is not worth it in most applications.

 

Because it is not effective in terms of Effort vs. Results.

 

But if you have free time - it will make your code cleaner. Maybe.

Share this post


Link to post
Share on other sites

Thanks, rgreat! Clean code is result of experience imho. I code for 3 years, and code of my first year is unclean for me )))

In langs like php it is unclear how to organize code, so MVC is applied as mostly simple and convenient.

MVC gives fixed structure but it also takes time to support this structure. I mean - sometimes it is easier to call some small method from view, but structure says - write methods in controller, bind it to view. And you have to.

My regards to all readers and writers of topic!

Don't hesistate to share your thoughts.

Share this post


Link to post
Share on other sites

Hi,

Thanks to all who had participate to this topic. Very interesting. So maybe as conclusion we could say:

Project: Complex, quality is the top priority (medicinal software, rocket steering ): Use MVC approach. Write test code for automatic test scenes ( "continous integration" ). If you are more then one developer team working on the same project, then this approach would recommended.

Project: Not very complicated, quality is important but not a killer argument: Use standard "Delphi" approach. 

 

OK?

Share this post


Link to post
Share on other sites

Hello,

 

For MVC the benefits would be great if the code is autogenerated instead of hand coded. Too much efforts needed to code by hand.

But code/form generator would benefit all kind of programming paradigm. Even more benefits with the Delphi RAD way.

Imagine that when we create a database application, we define datasets (sql, which fields shown in grid, which fields included in insert & edit, field labels, default values, etc) and a few clicks later, for each dataset it will autogenerate *.dfm and *.pas of:
- a datamodule that contains the dataset
- a form with dbgrid to browse, filter and search the dataset
- a form to do insert & edit the dataset

and those forms are ready to use and customize.

This would save time greatly :D

Share this post


Link to post
Share on other sites

"Too much efforts needed to code by hand"

In delphi Yes for the moment unfortunately, but thanks, delphi has its own structure, that can be used easily

 

"Imagine that when we create a database application, we define datasets "

such approach done in ASP MVC when there is DB context from Entity Framework which is alredy automates most of work...

 

Besides - do you use db aware or live bindings designer?

Share this post


Link to post
Share on other sites

A big reason to prefer more decoupled approaches such as MVC, MVP, MVVM, MV* is testability.  If all your logic is inside event handlers then typically it's quite hard to properly test and not possible to test entirely in isolation.  This may not be a show stopping problem but it is a consideration at least.  (Also see for example the "Humble Dialog" pattern and "Humble View".)

  • Upvote 1

Share this post


Link to post
Share on other sites
1 hour ago, wprins said:

A big reason to prefer more decoupled approaches such as MVC, MVP, MVVM, MV* is testability.  If all your logic is inside event handlers then typically it's quite hard to properly test and not possible to test entirely in isolation.  This may not be a show stopping problem but it is a consideration at least.  (Also see for example the "Humble Dialog" pattern and "Humble View".)

Absolutely!

Share this post


Link to post
Share on other sites

Unit testing quite easily maybe done with DUnitX.

But "Clean Code" and "Readability" - it is from practice...

Which code pattern to use is also good question...

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×