1

Applying MVC in Abap

 1 year ago
source link: https://blogs.sap.com/2022/10/18/applying-mvc-in-abap/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Applying MVC in Abap

0 1 121

In this blog post, I would like to share a few thoughts on how to write better code in SAP Abap by applying MVC to code structure.

I work as an Expert Abap consultant and sometimes fellow developers are asking advices on writing better code. There are many patterns, rules in coding world to help you to write better code. MVC is one of them, and I will try to explain where MVC fits in Abap development.

Previous to Abap, I worked with many different programming languages and my favorite languages are the object oriented ones, such as Java, C# and of course Abap. I like object oriented languages, because, you can create a model of the required algorithm in small modules, you can re-use code and encapsulate complexities. I believe, it is important to write lego like, modular, reusable code. That makes coding more fun and less painfull.

Below, we will see basics of MVC and then I will try to explain, the need for MVC with a basic scenario. And finally, we will see be how it can be applied in Abap with a mvc applied report code sample.

Let’s start with MVC word itself; MVC stands for Model View Controller pattern. It is used to separate layers from each other and also to make sure right code is place in right layer, in that way your application code is more robust, easier to extend, test and maintain.

And, the layers of MVC are, from top to bottom;

View -> In view layer you present/display your data. Reports, RFCs, Apis, Smartforms, ITS Dialog Screens can be part of this layer.

Controller -> In controller layer, requests are directed to related model method. Controller is the connection between view and model.  Controller can be a form procedure, method, function, just directs requests from view to Model. Should not contain any business logic in it.

Model -> Code where business logic is operated. Entry point of model can be a class method or a function. I prefer to use methods.

Screenshot_1-1.png

MVC Layers

For the sample scenario, let’s imagine we are requested to create an alv report to list the pending purchase order approvals. We need to display, PO number, Person to Approve, Date Sent for Approval, Number of days since PO sent for approval.

You got the related tables and bapis, you wrote the code under report include. Great! Quickly a solution is created. You could write code in a function or in a method of a class. But, there was no time, or that was a small development or just a simple piece of code, so code is just left under report.

Now, the requirement is, if number of days of waiting is more than 3 days, you need to send a reminder email to approver. Simple, just copy the code from program and paste it to another program and create a background job to send emails in given email format. With that development, now you have 2 programs.

And after sometime, someone asked you to create pdf output to send managers of approvers. Now you copy the code again, this time place it under code block of adobe pdf interface, done! Solution created.

And after sometime again, you are requested to implement a logic to check number of days from a Z table. instead of constant duration, 3 days, days are variable according material group and safety stock quantities.  There is a saying, “When a programmer first creates his code, only he and God know how it works, a few months down the line and only God knows“. You remember the last development maybe, or you may not be the developer of other programs and previous developer is gone. So You found out, there is a PDF form and a report. You have changed the code in those 2 areas. But background job is forgotten! First inconsistency.

This scenario can be extended with new requirements, new web apis, fiori apps, or for custom mobile apps. And each copy of the code is actually a base for inconsistency and extra work.

So what we have done as a fast solution, which took short time, can be very expensive at the end. Not just time while we are changing copies of code, but also in a critical scenario, inconsistencies may cause bigger problems than time. Therefore we need write code in a way, that we can re-use if we need, interface must not contain business logic ,but should be able to call function or better the method to retrieve required data and just display it.

In example above, report, email, pdf out are just interfaces. If the biz logic was implemented in a class. All interfaces could call class and get required data. And when you as a developer need to do a change, you would change that class, test that class. That is much easier, much more robust and definitely less time consuming, more extendible way of coding.

What I suggest is, try to implement MVC when you are coding, even in a small development. Once you change the way you think how to structure your programs with MVC, you will get better at that.

When you are trying to implement MVC, you may ask a question to yourself, in which layer should I put that piece of code now? Shall I keep it in interface or shall I move it to model class. Answer is simple, ask yourself, will I require that code if interface was different. If yes, place the code in lower layer, in model. In that way, you do not need to write same code for different interfaces.  If the answer is no, than just keep that code in interface.

And lets see the code samples for different types of developments, how MVC can be implemented.

Sample Report template;

Below template acts as two different reports/views. One of them shows data and user selects lines and creates wm transfer orders for selected ones. Or report can be set as background job with a variant and when job runs creates transfer orders for all records. Please check the code to see code reuse. One model class but two different views. That is because, business logic is not placed under report includes. If required a new program, web api, smartform or adobe form or any kind of interface can be created and can call the same model class.

Screen.png

I will place full code of report and related parts of class code, so that can remain here as reference.

Short info about report/view and class/model interaction ->  Report calls get_data method of class. get_data method acts as controller and forwards request to model, model object fills gt_recs table and that table is the source of ALV display.

If user_command button is clicked on screen with &createto function code, user command procedure forwards that request to model, acts as a controller.

*&---------------------------------------------------------------------*
*& Include ZBM_P_MVC_TEMPLATE- Report ZBM_P_MVC_TEMPLATE
*& Sample MVC implementation temaplate by BM
*& This report creates interface for users and also for background job
*& Calls bizobj methods to read data and displays data directly from
*& bizobj's public datas.
*&---------------------------------------------------------------------*

INCLUDE ZBM_P_MVC_TEMPLATE_TOP                          .    " Global Data
INCLUDE ZBM_P_MVC_TEMPLATE_F01                          .    " Controller

END-OF-SELECTION.

  PERFORM get_data.

  IF bizobj->gt_recs[] IS NOT INITIAL.
    IF p_backgr EQ abap_true.
      PERFORM background_process.
    ELSE.
      PERFORM display_data.
    ENDIF.

  ENDIF.     .  " FORM-Routines
*&---------------------------------------------------------------------*
*& Include ZBM_P_MVC_TEMPLATE_TOP                   - Report ZBM_P_MVC_TEMPLATE
*& Sample MVC implementation temaplate by BM
*& This include contains global data and selection screen definitions
*&---------------------------------------------------------------------*

REPORT zbm_p_mvc_template.

"Global data
TABLES: zwm_s_lt10_alv, lqua.
DATA: gv_grid TYPE REF TO cl_gui_alv_grid,
      bizobj  TYPE REF TO zbm_cl_mvc_template.

FIELD-SYMBOLS: <gs_selected_row> TYPE zwm_s_lt10_alv.

SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-t01.

  SELECT-OPTIONS: s_lgnum FOR lqua-lgnum OBLIGATORY,
                  s_lgtyp FOR lqua-lgtyp OBLIGATORY,
                  s_lgpla FOR lqua-lgpla,
                  s_matnr FOR lqua-matnr.

  PARAMETERS:
    p_bwlvs  TYPE ltak-bwlvs DEFAULT 999 OBLIGATORY,
    p_inglck TYPE xfeld DEFAULT abap_true.

SELECTION-SCREEN END OF BLOCK b1.

SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-t02.

  PARAMETERS:
    p_backgr TYPE xfeld. "Background processing request flag

SELECTION-SCREEN END OF BLOCK b2.


INITIALIZATION.
  bizobj = NEW zbm_cl_mvc_template( ).
*&---------------------------------------------------------------------*
*& Include          ZBM_P_MVC_TEMPLATE_F01
*& Sample MVC implementation temaplate by BM
*& This include contains Report / ALV related procedures
*& Also acts as controller of MVC implementation
*& by calling related methods of model class.
*&---------------------------------------------------------------------*


******************* Report Related Procedures - Begin **********************

TYPE-POOLS: slis.

"Field catalog, sütunlar için
DATA: gt_fieldcat TYPE slis_t_fieldcat_alv,
      gs_fieldcat TYPE LINE OF slis_t_fieldcat_alv,
      gt_layout   TYPE slis_layout_alv,
      g_variant   LIKE disvariant.


FORM set_grid_var.
  IF gv_grid IS INITIAL.
    "Getting the reference to teh ALV grid
    CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
      IMPORTING
        e_grid = gv_grid.
  ENDIF.
ENDFORM.


FORM prepare_field_catalog.
  "Prepare fields catalog
  IF gt_fieldcat IS INITIAL.

    CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
      EXPORTING
        i_program_name   = sy-repid
        i_structure_name = 'zwm_s_lt10_alv'
        i_inclname       = sy-repid
      CHANGING
        ct_fieldcat      = gt_fieldcat[].

    LOOP AT gt_fieldcat ASSIGNING FIELD-SYMBOL(<wa>)
      WHERE fieldname EQ 'IS_SELECTED'.
      <wa>-checkbox = abap_true.
      <wa>-no_out = abap_true.
    ENDLOOP.

  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  prepare_layout_alv
*&---------------------------------------------------------------------*
FORM prepare_layout_alv .
  gt_layout-colwidth_optimize = 'X'.
  gt_layout-zebra = 'X'.
  gt_layout-no_input = 'X'.
  gt_layout-box_fieldname = 'IS_SELECTED'.

ENDFORM.


FORM set_pf_status USING rt_extab TYPE slis_t_extab.
  "menüleri ve butonları standart olarak çıkarmayı sağlıyor.
  SET PF-STATUS 'STANDARD'.
  SET TITLEBAR 'TITLE1' WITH sy-uname.
ENDFORM.

FORM refresh_grid_data.
  "Refreshes the grid data
  PERFORM set_grid_var.
  CALL METHOD gv_grid->refresh_table_display.
ENDFORM.


FORM reload_data.
  "Verileri tekrar yükle
  PERFORM get_data.
  PERFORM refresh_grid_data.
ENDFORM.



"Display data
FORM display_data.
  PERFORM prepare_field_catalog.
  PERFORM prepare_layout_alv.

  IF g_variant IS INITIAL.
    g_variant-report   = sy-repid.
    g_variant-username = sy-uname.
  ENDIF.

  "Dipslay data
  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
    EXPORTING
      i_callback_program       = sy-repid
      i_callback_pf_status_set = 'SET_PF_STATUS'
      is_layout                = gt_layout
      it_fieldcat              = gt_fieldcat[]
      i_default                = 'X'
      i_save                   = 'A'
      is_variant               = g_variant
      i_callback_user_command  = 'USER_COMMAND'
    TABLES
      t_outtab                 = bizobj->gt_recs[].

  "Set grid var
  PERFORM set_grid_var.
ENDFORM.
******************* Report Related Procedures - End **********************



****************************** MVC Controller Part - Begin **********************

"ALV Komutları
FORM user_command USING r_ucomm
      LIKE sy-ucomm rs_selfield TYPE slis_selfield.

  CASE r_ucomm.

    WHEN '&CREATETO'.
      PERFORM create_to.

  ENDCASE.

ENDFORM. "USER_COMMAND

"Gets report data
FORM get_data.
  "Calls model class to retrieve report data
  "That is contoller method, simply passes view parameters to model
  "And retrieves data

  DATA: r_lgnum TYPE zwm_cl_lt10=>ty_r_lgnum,
        r_lgtyp TYPE zwm_cl_lt10=>ty_r_lgtyp,
        r_lgpla TYPE zwm_cl_lt10=>ty_r_lgpla,
        r_matnr TYPE zwm_cl_lt10=>ty_r_matnr.

  MOVE-CORRESPONDING s_lgnum[] TO r_lgnum[].
  MOVE-CORRESPONDING s_lgtyp[] TO r_lgtyp[].
  MOVE-CORRESPONDING s_lgpla[] TO r_lgpla[].
  MOVE-CORRESPONDING s_matnr[] TO r_matnr[].


  "Load excel data and display on ALV
  DATA(ls_status) = bizobj->get_data(
                      i_r_lgnum       = r_lgnum[]
                      i_r_lgtyp       = r_lgtyp[]
                      i_r_lgpla       = r_lgpla[]
                      i_r_matnr       = r_matnr[]
                      i_ignore_locked = p_inglck
                    ).
  IF ls_Status-status EQ zwm_cl_defs=>c_stat_success.
    DATA: lv_cnt TYPE i.
    lv_cnt = lines( bizobj->gt_recs[] ).
  ENDIF.

  MESSAGE ls_status-status_text TYPE 'S' DISPLAY LIKE ls_status-status.

ENDFORM.


FORM create_to.
  "Calls model class to Create transfer order in this example
  "That is contoller method, simply passes view parameters to model

  DATA: lv_ans2 TYPE char1.
  CLEAR lv_ans2.
  CALL FUNCTION 'POPUP_TO_CONFIRM'
    EXPORTING
      titlebar              = TEXT-tc1
      text_question         = TEXT-tc2
      text_button_1         = TEXT-tc3
      icon_button_1         = 'ICON_CHECKED'
      text_button_2         = TEXT-tc4
      icon_button_2         = 'ICON_CANCEL'
      display_cancel_button = ' '
      popup_type            = 'ICON_MESSAGE_ERROR'
    IMPORTING
      answer                = lv_ans2
    EXCEPTIONS
      text_not_found        = 1
      OTHERS                = 2.
  IF sy-subrc <> 0. ENDIF.

  IF lv_ans2 = '2'.
    RETURN.
  ENDIF.


  DATA(ls_state) = bizobj->create_transfer_orders(
                       i_bwlvs = p_bwlvs
                   ).

  PERFORM refresh_grid_data.

  MESSAGE ls_state-status_text TYPE 'S' DISPLAY LIKE ls_state-status.
ENDFORM.

FORM background_process.
  "Calls model class to Create transfer order in this example
  "That is contoller method, simply passes view parameters to model

  DATA(ls_state) = bizobj->create_transfer_orders_backg(
                       i_bwlvs = p_bwlvs
                   ).

  MESSAGE ls_state-status_text TYPE 'S' DISPLAY LIKE ls_state-status.
ENDFORM.

****************************** MVC Controller Part - End **********************

And model class code is below. Some part of the code is removed.

CLASS zbm_cl_mvc_template DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    "Types
    TYPES: BEGIN OF ty_status,
             status      TYPE zwm_e_status,
             status_text TYPE zwm_e_status_text,
           END OF ty_status.

    "Range Types
    TYPES: ty_R_lgnum TYPE RANGE OF lqua-lgnum,
           ty_R_lgtyp TYPE RANGE OF lqua-lgtyp,
           ty_R_lgpla TYPE RANGE OF lqua-lgpla,
           ty_R_matnr TYPE RANGE OF lqua-matnr.

    "Operation return status
    CONSTANTS c_stat_success TYPE zwm_e_status VALUE 'S' ##NO_TEXT.
    CONSTANTS c_stat_warning TYPE zwm_e_status VALUE 'W' ##NO_TEXT.
    CONSTANTS c_stat_error TYPE zwm_e_status VALUE 'E' ##NO_TEXT.
    CONSTANTS c_stat_info TYPE zwm_e_status VALUE 'I' ##NO_TEXT.


    DATA: gt_recs TYPE zwm_ty_s_lt10_alv.

    METHODS get_data
      IMPORTING
                VALUE(i_r_lgnum)       TYPE ty_r_lgnum
                VALUE(i_r_lgtyp)       TYPE ty_r_lgtyp
                VALUE(i_r_lgpla)       TYPE ty_r_lgpla
                VALUE(i_r_matnr)       TYPE ty_r_matnr
                VALUE(i_ignore_locked) TYPE xfeld OPTIONAL
      RETURNING VALUE(e_status)        TYPE ty_status.

    METHODS create_transfer_orders
      IMPORTING
                i_bwlvs         TYPE ltak-bwlvs OPTIONAL
      RETURNING VALUE(e_status) TYPE ty_status.

    METHODS create_transfer_orders_backg
      IMPORTING
                i_bwlvs         TYPE ltak-bwlvs OPTIONAL
      RETURNING VALUE(e_status) TYPE ty_status.

  PROTECTED SECTION.
  PRIVATE SECTION.

    METHODS set_status
      RETURNING VALUE(e_status) TYPE ty_Status.

    METHODS create_transfer_order
      IMPORTING
                i_bwlvs         TYPE ltak-bwlvs OPTIONAL
      CHANGING  c_rec           TYPE zwm_s_lt10_alv
      RETURNING VALUE(e_status) TYPE ty_Status.


ENDCLASS.



CLASS ZBM_CL_MVC_TEMPLATE IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZBM_CL_MVC_TEMPLATE->CREATE_TRANSFER_ORDER
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_BWLVS                        TYPE        LTAK-BWLVS(optional)
* | [<-->] C_REC                          TYPE        ZWM_S_LT10_ALV
* | [<-()] E_STATUS                       TYPE        TY_STATUS
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD create_transfer_order.
    DATA: exref TYPE REF TO cx_root.
    TRY.

        DATA: lt_return TYPE TABLE OF bapiret2.
        CALL FUNCTION 'L_TO_CREATE_SINGLE'
          EXPORTING
            i_bwlvs     .....

          IF c_rec-status_text IS INITIAL.
            MESSAGE s024(zwm) WITH c_rec-lgnum c_rec-tanum INTO c_rec-status_text.
          ENDIF.

        ENDIF.
      CATCH cx_root INTO exref.
        c_rec-status = zwm_cl_defs=>c_stat_error.
        c_rec-status_text = exref->get_text( ).
    ENDTRY.
  ENDMETHOD.                    "create_transfer_order


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBM_CL_MVC_TEMPLATE->CREATE_TRANSFER_ORDERS
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_BWLVS                        TYPE        LTAK-BWLVS(optional)
* | [<-()] E_STATUS                       TYPE        TY_STATUS
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD create_transfer_orders.
    DATA: exref TYPE REF TO cx_root.
    TRY.

        DATA lv_errored_recs TYPE i.
        LOOP AT gt_recs ASSIGNING FIELD-SYMBOL(<fs_rec>)
          WHERE is_selected EQ abap_true
            AND is_locked IS INITIAL.

          create_transfer_order(
            EXPORTING
              i_bwlvs  = i_bwlvs
            CHANGING
              c_rec    = <fs_rec>
          ).

          "Check all controls
          IF <fs_rec>-status NE c_stat_success.
            Lv_errored_recs = Lv_errored_recs + 1.
          ENDIF.

        ENDLOOP.
        IF sy-subrc IS INITIAL.
          IF lv_errored_recs EQ 0.
            e_status-status = zwm_cl_defs=>c_stat_success.
            MESSAGE s029(zwm) INTO e_status-status_text.
          ELSE.
            e_status-status = zwm_cl_defs=>c_stat_warning.
            MESSAGE s014(zwm) WITH lv_errored_recs INTO e_status-status_text.
          ENDIF.
        ELSE.
          e_status-status = zwm_cl_defs=>c_stat_warning.
          MESSAGE s026(zwm) INTO e_status-status_text.
        ENDIF.

      CATCH cx_root INTO exref.
        e_status-status = c_stat_error.
        e_status-status_text = exref->get_text( ).
    ENDTRY.

  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBM_CL_MVC_TEMPLATE->CREATE_TRANSFER_ORDERS_BACKG
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_BWLVS                        TYPE        LTAK-BWLVS(optional)
* | [<-()] E_STATUS                       TYPE        TY_STATUS
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD create_transfer_orders_backg.
    DATA: exref TYPE REF TO cx_root.
    TRY.

        LOOP AT gt_recs ASSIGNING FIELD-SYMBOL(<wa>).
          <wa>-is_selected = abap_true.
        ENDLOOP.

        create_transfer_orders(
            i_bwlvs = i_bwlvs
        ).

      CATCH cx_root INTO exref.
        e_status-status = c_stat_error.
        e_status-status_text = exref->get_text( ).
    ENDTRY.
  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZBM_CL_MVC_TEMPLATE->GET_DATA
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_R_LGNUM                      TYPE        TY_R_LGNUM
* | [--->] I_R_LGTYP                      TYPE        TY_R_LGTYP
* | [--->] I_R_LGPLA                      TYPE        TY_R_LGPLA
* | [--->] I_R_MATNR                      TYPE        TY_R_MATNR
* | [--->] I_IGNORE_LOCKED                TYPE        XFELD(optional)
* | [<-()] E_STATUS                       TYPE        TY_STATUS
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD get_data.
    DATA: exref TYPE REF TO cx_root.
    TRY.
        "Get pending records of LT10
        SELECT *
          INTO CORRESPONDING FIELDS OF TABLE gt_recs
          FROM lqua ....

        IF gt_recs IS NOT INITIAL.
          e_status-status = c_stat_success.
          DATA: lv_cnt TYPE i.
          lv_cnt = lines( gt_recs ).
          MESSAGE s027(zwm) WITH lv_cnt INTO e_status-status_text.
        ELSE.
          e_status-status = c_stat_warning.
          MESSAGE s028(zwm) INTO e_status-status_text.
        ENDIF.

      CATCH cx_root INTO exref.
        e_status-status = c_stat_error.
        e_status-status_text = exref->get_text( ).
    ENDTRY.

  ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZBM_CL_MVC_TEMPLATE->SET_STATUS
* +-------------------------------------------------------------------------------------------------+
* | [<-()] E_STATUS                       TYPE        TY_STATUS
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD set_status.
    DATA: exref TYPE REF TO cx_root.
    TRY.

        IF gt_recs IS NOT INITIAL.

          SELECT .......
        ENDIF.

        e_status-status = c_stat_success.

      CATCH cx_root INTO exref.
        e_status-status = c_stat_error.
        e_status-status_text = exref->get_text( ).
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

So that was a basic example of ,how to write an abap report by applying MVC pattern. MVC enables us to split view and business logic code. We can re-use model class for different types of interfaces. With fiori projects, frontend and backend development can be split and MVC enables us to share tasks. One can write backend and other can write front end.

Depending on interest, I can add more mvc templates, such as MVC ITS Dialog screen code or adobe form with MVC. The logic is same, you can apply mvc to any code.

And if you like the idea of lego like ,reusable and robust coding, you must check solid rules too. Here is a link for solid. And a link for clean abap.

I hope that gives some of you an idea about MVC in Abap. Please feel free to add your own examples in comments.

Thanks for reading.


Recommend

  • 75
    • 掘金 juejin.im 6 years ago
    • Cache

    深入分析MVC、MVP、MVVM、VIPER

    前言看了下上篇博客的发表时间到这篇博客,竟然过了11个月,罪过,罪过。这一年时间也是够折腾的,年初离职跳槽到鹅厂,单独负责一个社区项目,忙的天昏地暗,忙的差不多了,转眼就到了7月。 七月流火,心也跟着燥热起来了,眼瞅着移动端这发展趋势从05年开始就一...

  • 66
    • 微信 mp.weixin.qq.com 6 years ago
    • Cache

    你真的理解了MVC, MVP, MVVM吗?

  • 77
    • 微信 mp.weixin.qq.com 6 years ago
    • Cache

    你真的理解了 MVC, MVP, MVVM 吗?

    你真的理解了 MVC, MVP, MVVM 吗?

  • 48
    • 微信 mp.weixin.qq.com 6 years ago
    • Cache

    Spring MVC 温故而知新:从零开始

    Spring MVC 温故而知新:从零开始

  • 67
    • 掘金 juejin.im 6 years ago
    • Cache

    MVC、MVP、MVVM,我到底该怎么选?

    本文由玉刚说写作平台提供写作赞助 原作者:AndroFarmer 版权声明:本文版权归微信公众号玉刚说所有,未经许可,不得以任何形式转载 前言 MVC、MVP、MVVM是我们工作和面试中都比较重要的一块,但很多时候我们却有点迷惑。比如看了好多篇文章都搞不懂M

  • 30
    • studygolang.com 6 years ago
    • Cache

    小探MVC模式

    本文主要简单介绍一个MVC模式。 1、最近在看一个golang的框架(beego),因此对 MVC模式 有了一个基本的认识。简单来说,MVC模式是 架构模式 中的一种,也是最常用的一种,很过web框架...

  • 39
    • www.kodnito.com 6 years ago
    • Cache

    Getting Started With MVC 1.0 (JSR 371)

    Getting Started With MVC 1.0 (JSR 371), Thymeleaf, H2 Database, JPA, Hibernate, Thorntail

  • 36
    • www.tuicool.com 5 years ago
    • Cache

    Typescript-ExpressJS MVC pattern

    ExpressJS-Typescript-Project-Template MVC Patterned Typescript - ExpressJS Starter Projector Before Start $ npm install or $ yarn Start $ npm...

  • 39
    • www.tuicool.com 5 years ago
    • Cache

    Spring 源码学习(十) Spring mvc

    经过前面的 AOP (面向切面编程) 和 Transaction (事务管理),这次来到了 MVC (Web 应用,进行请求分发和处理) Spring MVC 定义: 分离了控制器(Controller)、模...

  • 58
    • www.tuicool.com 5 years ago
    • Cache

    Spring MVC Framework Tutorial

    Spring MVC helps in building flexible and loosely coupled web applications. The Model-view-controller design pattern helps in seperating the business logic, presentation logic, and navigation logic. Models

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK