objets métiers -> base de données

objets métiers -> base de données - SQL/NoSQL - Programmation

Marsh Posté le 05-07-2007 à 10:27:23    

Bonjour,
je souhaite faire un petit programme de gestion orienté objet avec utilisation d'une base de données pour sauvegarder les données. Je n'utilise pas d'outils de mapping objet/relationnel.
 
J'ai défini les objets métiers. Je souhaite savoir s'il doivent disposer eux-mêmes de méthodes pour l'insertion ou la séléction dans la base de données ou si je dois faire intervenir un élement extérieur.
Evidement les deux approches sont possibles mais je voudrais vos avis sur la pertinence des deux stratégies.
Merci.

Reply

Marsh Posté le 05-07-2007 à 10:27:23   

Reply

Marsh Posté le 05-07-2007 à 11:30:33    

Je vois pas bien quel "element extérieur" tu voudrais faire intervenir ?


---------------
When it's from Finland it's good.  - Mon blog
Reply

Marsh Posté le 05-07-2007 à 11:32:54    

L'objet qui me permet de faire le lien entre l'interface graphique et les objets métiers par exemple.

Reply

Marsh Posté le 05-07-2007 à 19:29:19    

Il existe effectivement plusieurs méthodes.
 
1/ tu te trimbales l'ensemble du code nécessaire aux accès dans la base de données dans tes objets métiers.
Avantages : Les objets métiers sont autonomes, et tu n'as pas à te soucier de la présence d'élément extérieur.
Inconvénients : Si la structure de la base de données change, ou si la méthode d'accès à cette dernière change, ou si tu veux mettre en place des solutions de cache évoluées, ou autres, tu vas être obligé de modifier énormément de code redondant, voir te heurter à des problèmes quasi insollubles
 
2/ Utiliser un objet d'accès à la base de données très basique (pour ainsi dire, une simple surcharge des méthodes des objets de Base du Framework), et les appeler depuis tes objets métiers.
Avantages : Moins de redondance que dans le premier cas, et maintenabilité accrue
Inconvénients : Les objets métiers font toujours des appels à des méthodes assez bas niveau, ce qui peut compromettre en partie l'évolutivité (si demain tu changes d'un système de SGBD pour une solution basée sur des fichiers plats, tes objets métier sont tout aussi impactés que dans le premier cas)
 
3/ Déporter complètement la couche "accès aux données" vers l'objet de base de données. Ainsi, l'objet de base de données contient non seulement la connection à la base, mais toutes les méthodes métiers permettant l'accès à la base.
Avantages : Abstraction totale de la couche des données. Ainsi la maintenance et l'évolutivité sont très aisées
Inconvénients : Déport de méthodes métier au sein d'un objet "générique". Ainsi, l'étude de l'objet métier seul est insuffisant pour connaître tous les tenants du fonctionnement de ce dernier.
 
4/ Incorporer les deux couches ensemble, grace à l'héritage. l'objet métier dispose des méthodes de base d'accès à la couche des données, tandis que l'objet métier en hérite afin de faire lui-même les traîtements dans la base, sans se soucier de la façon qu'il les fait
Avantages : à priori, tous les avantages des 3 solutions précédentes
Inconvéniets : à priori, aucun des inconvénients cités ci-dessus
 
Personnellement, j'utilise actuellement le système "3". Il est aisé à mettre en place, et facilement maintenable lorsque les objets métiers restent simples et peu nombreux.
 
Je viens d'imaginer le cas "4", qui me semble en tout points suppérieur. Mais vu que je viens de l'imaginer, je peux pas te garantir qu'il marche :D


Message édité par MagicBuzz le 05-07-2007 à 19:31:01
Reply

Marsh Posté le 05-07-2007 à 20:58:32    

Je viens de tester en vitesse une implémentation pourrave du 4.
Spa trop pourri, faut juste pas avoir l'esprit tordu comme moi :D
 
La base de test

Code :
  1. CREATE TABLE produit (code varchar(16) PRIMARY KEY NOT NULL, nom varchar(50) NOT NULL)
  2. go
  3.  
  4. INSERT INTO produit (code, nom) VALUES ('P1', 'Produit 1');
  5. INSERT INTO produit (code, nom) VALUES ('P2', 'Produit 2');
  6. INSERT INTO produit (code, nom) VALUES ('P3', 'Nutella');


 
Le code C#

Code :
  1. using System;
  2.  
  3. namespace Couches
  4. {
  5.    class Program
  6.    {
  7.        static void Main(string[] args)
  8.        {
  9.            Console.WriteLine("Création de l'objet métier \"Produit\"." );
  10.            Produit p = new Produit();
  11.  
  12.            Console.WriteLine("Chargement des produits dont le nom commence par \"Produit\"." );
  13.            p.LoadProducts("Produit%" );
  14.            p.DisplayProducts();
  15.  
  16.            Console.WriteLine("Chargement du produit \"P1\" et renommage." );
  17.            p.LoadProduct("P1" );
  18.            p.UpdateProductName("Produit 1 renommé" );
  19.            p.DisplayProducts();
  20.  
  21.            Console.WriteLine("Chargement de tous les produits et tentative de mise à jour abusive." );
  22.            p.LoadProducts(string.Empty);
  23.            p.UpdateProductName("Test..." );
  24.            p.DisplayProducts();
  25.  
  26.            Console.WriteLine("Chargement de tous les produits dont le nom est \"Produit 2\"" );
  27.            Console.WriteLine("et mise à jour (si un seul produit)." );
  28.            p.LoadProducts("Produit 2" );
  29.            p.UpdateProductName("Test..." );
  30.            p.DisplayProducts();
  31.  
  32.            Console.ReadKey();
  33.        }
  34.    }
  35. }
  36.  
  37.  
  38. using System;
  39. using System.Data;
  40.  
  41. namespace Couches
  42. {
  43.    class Produit : DataBase
  44.    {
  45.        private DataTable dt;
  46.  
  47.        public Produit()
  48.        {
  49.            // Le constructeur de "DataBase" est implicitement appelé
  50.            
  51.            dt = new DataTable();
  52.        }
  53.  
  54.        public bool LoadProduct(string productcode)
  55.        {
  56.            if (Open())
  57.            {
  58.                object[,] filters = new object[1, 2];
  59.                filters[0,0] = "code";
  60.                filters[0,1] = productcode;
  61.                dt = LoadTable("produit", filters);
  62.                if (dt != null && dt.Rows.Count == 1)
  63.                    return true;
  64.            }
  65.            return false;
  66.        }
  67.  
  68.        public bool UpdateProductName(string productname)
  69.        {
  70.            if (Open() && dt != null && dt.Rows.Count == 1)
  71.            {
  72.                dt.Rows[0]["nom"] = productname;
  73.                if (SaveTable(dt) == 1)
  74.                    return true;
  75.            }
  76.            return false;
  77.        }
  78.  
  79.        public int LoadProducts(string namefilter)
  80.        {
  81.            if (!Open())
  82.            {
  83.                return -1;
  84.            }
  85.  
  86.            object[,] filters;
  87.  
  88.            if (namefilter.Length > 0)
  89.            {
  90.                filters = new object[1, 2];
  91.                filters[0, 0] = "nom";
  92.                filters[0, 1] = namefilter;
  93.                dt = LoadTable("produit", filters);
  94.            }
  95.            else
  96.            {
  97.                filters = new object[0, 0];
  98.            }
  99.  
  100.            dt = LoadTable("produit", filters);
  101.            if (dt != null)
  102.            {
  103.                return dt.Rows.Count;
  104.            }
  105.            else
  106.            {
  107.                return -1;
  108.            }
  109.        }
  110.  
  111.        public void DisplayProducts()
  112.        {
  113.            Console.WriteLine("--------------------------------------" );
  114.            if (dt != null)
  115.            {
  116.                for (int i = 0, cpt = dt.Rows.Count; i < cpt; i++)
  117.                {
  118.                    Console.WriteLine("Code : {0}", dt.Rows[i]["code"] as string);
  119.                    Console.WriteLine("Nom :  {0}", dt.Rows[i]["nom"] as string);
  120.                }
  121.            }
  122.            else
  123.            {
  124.                Console.WriteLine("Aucun produit chargé..." );
  125.            }
  126.            Console.WriteLine("--------------------------------------" );
  127.            Console.WriteLine();
  128.        }
  129.    }
  130. }
  131.  
  132.  
  133. using System;
  134. using System.Data;
  135. using System.Data.SqlClient;
  136. using System.Text;
  137.  
  138. namespace Couches
  139. {
  140.    class DataBase
  141.    {
  142.        private const string ConnectionString = "server=.;database=test;Integrated Security=SSPI";
  143.        private SqlConnection cnx;
  144.        private SqlDataAdapter da;
  145.  
  146.        protected DataBase()
  147.        {
  148.            cnx = new SqlConnection(ConnectionString);
  149.        }
  150.  
  151.        protected bool Open()
  152.        {
  153.            if ((cnx.State == ConnectionState.Open))
  154.            {
  155.                return true;
  156.            }
  157.  
  158.            try
  159.            {
  160.                cnx.Open();
  161.            }
  162.            catch
  163.            {
  164.                return false;
  165.            }
  166.  
  167.            if (cnx.State == ConnectionState.Open)
  168.            {
  169.                return true;
  170.            }
  171.            else
  172.            {
  173.                return false;
  174.            }
  175.  
  176.        }
  177.  
  178.        protected void Close()
  179.        {
  180.            try
  181.            {
  182.                cnx.Close();
  183.            }
  184.            catch { }
  185.        }
  186.  
  187.        protected DataTable LoadTable(string TableName, object[,] Filters)
  188.        {
  189.            if (!(cnx.State == ConnectionState.Open))
  190.            {
  191.                return null;
  192.            }
  193.  
  194.            StringBuilder sb = new StringBuilder(string.Format("select * from {0} where null is null", TableName));
  195.            SqlCommand cmd = cnx.CreateCommand();
  196.            cmd.CommandType = CommandType.Text;
  197.  
  198.            for (int i = 0, cpt = Filters.GetLength(0); i < cpt; i++)
  199.            {
  200.                if (Filters[i, 1].GetType().Name == "String" && (Filters[i, 1] as string).IndexOf('%') > -1)
  201.                {
  202.                    sb.Append(string.Format(" and {0} like @{0}", Filters[i, 0] as string));
  203.                }
  204.                else
  205.                {
  206.                    sb.Append(string.Format(" and {0} = @{0}", Filters[i, 0] as string));
  207.                }
  208.                SqlParameter p = cmd.CreateParameter();
  209.                p.Direction = ParameterDirection.Input;
  210.                p.ParameterName = Filters[i, 0] as string;
  211.                p.SqlValue = Filters[i, 1];
  212.                cmd.Parameters.Add(p);
  213.            }
  214.  
  215.            cmd.CommandText = sb.ToString();
  216.            da = new SqlDataAdapter(cmd);
  217.            SqlCommandBuilder cb = new SqlCommandBuilder(da);
  218.            da.InsertCommand = cb.GetInsertCommand();
  219.            da.UpdateCommand = cb.GetUpdateCommand();
  220.            da.DeleteCommand = cb.GetDeleteCommand();
  221.            DataTable dt = new DataTable();
  222.            da.Fill(dt);
  223.            return dt;
  224.        }
  225.  
  226.        protected int SaveTable(DataTable dt)
  227.        {
  228.            if (!(cnx.State == ConnectionState.Open))
  229.            {
  230.                return -1;
  231.            }
  232.  
  233.            DataTable Changes = dt.GetChanges();
  234.            int NbChanges = 0;
  235.  
  236.            if (dt != null && dt.Rows.Count > 0)
  237.            {
  238.                NbChanges = da.Update(Changes);
  239.                dt.AcceptChanges();
  240.            }
  241.  
  242.            return NbChanges;
  243.        }
  244.    }
  245. }


 
Sortie :


Création de l'objet métier "Produit".
Chargement des produits dont le nom commence par "Produit".
--------------------------------------
Code : P1
Nom :  Produit 1
Code : P2
Nom :  Produit 2
--------------------------------------
 
Chargement du produit "P1" et renommage.
--------------------------------------
Code : P1
Nom :  Produit 1 renommé
--------------------------------------
 
Chargement de tous les produits et tentative de mise à jour abusive.
--------------------------------------
Code : P1
Nom :  Produit 1 renommé
Code : P2
Nom :  Produit 2
Code : P3
Nom :  Nutella
--------------------------------------
 
Chargement de tous les produits dont le nom est "Produit 2"
et mise à jour (si un seul produit).
--------------------------------------
Code : P2
Nom :  Test...
--------------------------------------


 
 
Edit : Plus je me relis, et plus je me dis que c'est total n'imp' mon objet "Produit" :D M'enfin t'as le principe, et il marche ;)
 
On voit notamment très bien dans cet exemple que :
- DataBase ne sais absolument pas ce qu'il fait. Il est complètement générique, et ne contient aucune information relative aux métiers
- On voit aussi que l'objet métier ne sait pas non plus d'où viennent les données et ce qu'on en fait
 
=> En quelques heures, je peux altérer mon objet "DataBase" pour passer à une base Oracle par exemple, ou même la gestion d'information à partir de fichiers XML


Message édité par MagicBuzz le 05-07-2007 à 21:05:46
Reply

Marsh Posté le 06-07-2007 à 08:26:45    

Merci beaucoup MagicBuzz, c'est exactement ce genre de réponse que j'attendais. Bravo pour la précision de tes explications et pour ton code qui m'a aidé à refaire mon programme proprement et plus efficacement.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed