Le Singleton Pattern dans RAP permet de créer des Business Objects qui n’ont qu’une seule instance. C’est idéal pour les paramètres applicatifs, configurations ou paramètres globaux qui doivent être gérés de manière centralisée.
Concept de base
Un Singleton dans le contexte RAP est un Business Object qui :
- Ne peut avoir qu’une seule instance
- Ne peut pas être supprimé (l’instance existe toujours)
- Ne permet pas la création de nouvelles instances
- N’autorise que les opérations UPDATE
| BO normal | BO Singleton |
|---|---|
| Nombre d’instances illimité | Exactement une instance |
| CREATE, UPDATE, DELETE | Uniquement UPDATE |
| Clé propre | Clé fixe/virtuelle |
| Liste d’objets | Un objet global |
Cas d’utilisation
Le Singleton Pattern convient pour :
- Paramètres système : Paramètres de configuration globaux
- Configuration applicative : Feature flags, seuils
- Paramètres mandant : Valeurs par défaut spécifiques à l’entreprise
- Compteurs et statistiques : Métriques applicatives
- Dernier état : Objets “Current” (période de facturation actuelle)
Implémentation
1. Table de base de données
La table a généralement une valeur de clé fixe :
@EndUserText.label : 'Application Settings"@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE@AbapCatalog.tableCategory : #TRANSPARENT@AbapCatalog.deliveryClass : #Cdefine table zappsettings { key client : abap.clnt not null; key singleton_key : abap.char(1) not null; " Toujours 'X" max_items : abap.int4; default_currency : abap.cuky; enable_feature_a : abap_boolean; enable_feature_b : abap_boolean; last_changed_at : timestampl; last_changed_by : abap.uname;}2. CDS View pour le Singleton
La différence clé est l’annotation @ObjectModel.singleton.element :
@AccessControl.authorizationCheck: #NOT_REQUIRED@EndUserText.label: 'Application Settings"define root view entity ZI_AppSettings as select from zappsettings{ key singleton_key as SingletonKey,
max_items as MaxItems, default_currency as DefaultCurrency, enable_feature_a as EnableFeatureA, enable_feature_b as EnableFeatureB,
@Semantics.systemDateTime.lastChangedAt: true last_changed_at as LastChangedAt,
@Semantics.user.lastChangedBy: true last_changed_by as LastChangedBy}3. Behavior Definition avec Singleton
La Behavior Definition déclare le Singleton avec le mot-clé singleton :
managed implementation in class zbp_i_appsettings unique;strict ( 2 );
define behavior for ZI_AppSettings alias Settingspersistent table zappsettingslock masterauthorization master ( instance ){ // Déclaration du Singleton static singleton;
// Uniquement Update autorisé - pas de Create/Delete update;
// Mapping des champs field ( readonly ) SingletonKey; field ( readonly ) LastChangedAt, LastChangedBy;
// Remplissage automatique des champs mapping for zappsettings corresponding { SingletonKey = singleton_key; MaxItems = max_items; DefaultCurrency = default_currency; EnableFeatureA = enable_feature_a; EnableFeatureB = enable_feature_b; LastChangedAt = last_changed_at; LastChangedBy = last_changed_by; }}4. Initialisation automatique du Singleton
Comme un Singleton doit toujours exister, il est créé automatiquement lors du premier accès. Cela se fait via une extension read :
managed implementation in class zbp_i_appsettings unique;strict ( 2 );
define behavior for ZI_AppSettings alias Settingspersistent table zappsettingslock masterauthorization master ( instance ){ static singleton;
update;
// Permet l'initialisation automatique internal create;
field ( readonly ) SingletonKey;}5. Implémentation du Behavior
L’implémentation assure que le Singleton existe :
CLASS zbp_i_appsettings DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zi_appsettings.ENDCLASS.
CLASS zbp_i_appsettings IMPLEMENTATION.ENDCLASS.
CLASS lhc_settings DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR Settings RESULT result.
METHODS read FOR READ IMPORTING keys FOR READ Settings RESULT result.ENDCLASS.
CLASS lhc_settings IMPLEMENTATION.
METHOD get_instance_authorizations. " Tous peuvent lire et modifier (ajouter le contrôle d'autorisation ici) LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>). APPEND VALUE #( %tky = <key>-%tky %update = if_abap_behv=>auth-allowed ) TO result. ENDLOOP. ENDMETHOD.
METHOD read. " Vérifier si le Singleton existe SELECT SINGLE * FROM zappsettings WHERE singleton_key = 'X" INTO @DATA(ls_settings).
IF sy-subrc <> 0. " Le Singleton n'existe pas - créer initialement INSERT zappsettings FROM @( VALUE #( singleton_key = 'X" max_items = 100 default_currency = 'EUR" ) ).
" Relire SELECT SINGLE * FROM zappsettings WHERE singleton_key = 'X" INTO @ls_settings. ENDIF.
" Retourner le résultat result = VALUE #( ( SingletonKey = ls_settings-singleton_key MaxItems = ls_settings-max_items DefaultCurrency = ls_settings-default_currency EnableFeatureA = ls_settings-enable_feature_a EnableFeatureB = ls_settings-enable_feature_b LastChangedAt = ls_settings-last_changed_at LastChangedBy = ls_settings-last_changed_by ) ). ENDMETHOD.
ENDCLASS.Singleton avec Draft
Pour des paramètres plus complexes, le Draft Handling peut être activé :
managed implementation in class zbp_i_appsettings unique;strict ( 2 );with draft;
define behavior for ZI_AppSettings alias Settingspersistent table zappsettingsdraft table zappsettings_dlock masterauthorization master ( instance ){ static singleton;
update; internal create;
draft action Edit; draft action Activate optimized; draft action Discard; draft action Resume; draft determine action Prepare;
field ( readonly ) SingletonKey;}La table Draft :
@EndUserText.label : 'Draft: Application Settings"@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE@AbapCatalog.tableCategory : #TRANSPARENTdefine table zappsettings_d { key client : abap.clnt not null; key singleton_key : abap.char(1) not null; max_items : abap.int4; default_currency : abap.cuky; enable_feature_a : abap_boolean; enable_feature_b : abap_boolean; // Champs Admin Draft "%admin" : include sych_bdl_draft_admin_inc;}Projection View
La Projection pour l’application Fiori :
@AccessControl.authorizationCheck: #NOT_REQUIRED@EndUserText.label: 'App Settings"@Metadata.allowExtensions: truedefine root view entity ZC_AppSettings provider contract transactional_query as projection on ZI_AppSettings{ key SingletonKey,
MaxItems, DefaultCurrency, EnableFeatureA, EnableFeatureB,
LastChangedAt, LastChangedBy}Projection Behavior
projection;strict ( 2 );use draft;
define behavior for ZC_AppSettings alias Settings{ use update;
use action Edit; use action Activate; use action Discard; use action Resume; use action Prepare;}UI-Annotation pour Object Page
Un Singleton est généralement affiché comme Object Page sans liste préalable :
@Metadata.layer: #CUSTOMERannotate entity ZC_AppSettings with{ @UI.facet: [ { id: 'GeneralSettings', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, label: 'Paramètres généraux', position: 10, targetQualifier: 'General" }, { id: 'Features', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, label: 'Feature Flags', position: 20, targetQualifier: 'Features" } ]
@UI.fieldGroup: [{ qualifier: 'General', position: 10 }] MaxItems;
@UI.fieldGroup: [{ qualifier: 'General', position: 20 }] DefaultCurrency;
@UI.fieldGroup: [{ qualifier: 'Features', position: 10 }] EnableFeatureA;
@UI.fieldGroup: [{ qualifier: 'Features', position: 20 }] EnableFeatureB;}Consommer le Singleton depuis le code
Accès EML
" Lire le SingletonREAD ENTITIES OF zi_appsettings ENTITY Settings ALL FIELDS WITH VALUE #( ( SingletonKey = 'X' ) ) RESULT DATA(lt_settings).
DATA(ls_settings) = lt_settings[ 1 ].
" Modifier le SingletonMODIFY ENTITIES OF zi_appsettings ENTITY Settings UPDATE FIELDS ( MaxItems EnableFeatureA ) WITH VALUE #( ( SingletonKey = 'X" MaxItems = 200 EnableFeatureA = abap_true ) ) FAILED DATA(failed) REPORTED DATA(reported).
" SauvegarderCOMMIT ENTITIES.Classe Helper pour un accès simplifié
CLASS zcl_app_settings DEFINITION PUBLIC FINAL CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_app_settings,
get_max_items RETURNING VALUE(rv_value) TYPE i,
get_default_currency RETURNING VALUE(rv_value) TYPE waers,
is_feature_a_enabled RETURNING VALUE(rv_enabled) TYPE abap_bool.
PRIVATE SECTION. CLASS-DATA: go_instance TYPE REF TO zcl_app_settings. DATA: ms_settings TYPE zi_appsettings.
METHODS constructor. METHODS load_settings.ENDCLASS.
CLASS zcl_app_settings IMPLEMENTATION.
METHOD get_instance. IF go_instance IS NOT BOUND. go_instance = NEW #( ). ENDIF. ro_instance = go_instance. ENDMETHOD.
METHOD constructor. load_settings( ). ENDMETHOD.
METHOD load_settings. READ ENTITIES OF zi_appsettings ENTITY Settings ALL FIELDS WITH VALUE #( ( SingletonKey = 'X' ) ) RESULT DATA(lt_settings).
IF lt_settings IS NOT INITIAL. ms_settings = lt_settings[ 1 ]. ENDIF. ENDMETHOD.
METHOD get_max_items. rv_value = get_instance( )->ms_settings-MaxItems. ENDMETHOD.
METHOD get_default_currency. rv_value = get_instance( )->ms_settings-DefaultCurrency. ENDMETHOD.
METHOD is_feature_a_enabled. rv_enabled = get_instance( )->ms_settings-EnableFeatureA. ENDMETHOD.
ENDCLASS.Singleton avec entité parente
Un Singleton peut également exister en tant qu’enfant d’un autre BO :
define behavior for ZI_Project alias Project{ create; update; delete;
// Chaque projet a exactement une instance Settings association _ProjectSettings { create; }}
define behavior for ZI_ProjectSettings alias ProjectSettingslock dependent by _Projectauthorization dependent by _Project{ // Singleton par parent singleton;
update; internal create;
association _Project;
field ( readonly ) ProjectID;}Bonnes pratiques
| Recommandation | Justification |
|---|---|
| Utiliser une valeur de clé fixe | Accès simple sans recherche |
Utiliser internal create | Permettre l’initialisation automatique |
| Fournir une classe Helper | Accès simplifié depuis le code ABAP |
| Implémenter un cache | Performance lors d’accès fréquents |
| Vérifier les autorisations | Tout le monde ne devrait pas pouvoir modifier les paramètres |
| Draft pour paramètres complexes | Meilleure UX avec de nombreux paramètres |
Résumé
Le Singleton Pattern dans RAP est la solution idéale pour :
- Configurations globales gérées de manière centralisée
- Feature Flags pour contrôler le comportement applicatif
- Paramètres système qui ne doivent exister qu’une seule fois
Avec le mot-clé static singleton dans la Behavior Definition et internal create pour l’initialisation automatique, l’implémentation est simple et directe.
Sujets connexes
- Fondamentaux RAP - Concepts de base de RAP
- Draft Handling dans RAP - Comprendre la sauvegarde intermédiaire
- Feature Control & Side Effects - Contrôle dynamique de l’UI