Programmation objets intuitif ?

Signaler
Messages postés
403
Date d'inscription
vendredi 28 octobre 2005
Statut
Membre
Dernière intervention
31 août 2008
-
Messages postés
403
Date d'inscription
vendredi 28 octobre 2005
Statut
Membre
Dernière intervention
31 août 2008
-
Bonjour,

voilà plusieurs mois que j'ai appris la programmation avec C# et je dois dire que je n'ai toujours pas les reflexes pour "programmer objet". Quand je crée un programme, je n'arrive pas à m'imaginer quelles seront les classes d'objets dont j'aurais besoin. J'ai beaucoup plus de facilité à résonner avec des procédures. Voici par exemple un petit programme que j'ai fait. Il compare des fichiers dans 2 dossiers et affiche les fichiers qui existent dans le dossier 1 mais pas dans le 2, les fichiers qui existent dans le 2 mais pas dans le 1, les fichiers qui existent dans les 2 mais dont les contenus sont différents, les fichiers identiques. J'aurais aimé savoir si une approche "objet" était possible pour traiter ce problème ? Voici de mon côté comment j'ai procédé :

namespace Compare
{
public partial class Form1 : Form
{
public ArrayList PasDans1 = new ArrayList();
public ArrayList PasDans2 = new ArrayList();
public ArrayList Differents = new ArrayList();
public ArrayList Identiques = new ArrayList();

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
textBox3.Text = "";
DirectoryInfo Folder1, Folder2;
try { Folder1 = new DirectoryInfo(textBox1.Text); }
catch
{
MessageBox.Show("Chemin 1 incorrect !", "ATTENTION !", MessageBoxButtons.OK);
goto end;
}
try { Folder2 = new DirectoryInfo(textBox2.Text); }
catch
{
MessageBox.Show("Chemin 2 incorrect !", "ATTENTION !", MessageBoxButtons.OK);
goto end;
}
FileInfo[] MyFileInfoList1 = Folder1.GetFiles();
FileInfo[] MyFileInfoList2 = Folder2.GetFiles();

foreach (FileInfo MyFileInfo in MyFileInfoList1)
{
if (Contains(MyFileInfoList2, MyFileInfo))
{
if (Read(textBox1.Text + "\" + MyFileInfo.Name) != Read(textBox2.Text + "\" + MyFileInfo.Name))
Differents.Add(MyFileInfo.Name);
else
Identiques.Add(MyFileInfo.Name);
}
else
{
PasDans2.Add(MyFileInfo.Name);
}
}
foreach (FileInfo MyFileInfo in MyFileInfoList2)
{
if (!Contains(MyFileInfoList1, MyFileInfo))
PasDans1.Add(MyFileInfo.Name);
}
string[] notin1 = new string[PasDans1.Count];
PasDans1.CopyTo(notin1);
string[] notin2 = new string[PasDans2.Count];
PasDans2.CopyTo(notin2);
string[] diff = new string[Differents.Count];
Differents.CopyTo(diff);
string[] id = new string[Identiques.Count];
Identiques.CopyTo(id);
textBox3.Text = "Fichiers non présents dans le dossier 1 :\r\n" +
System.String.Join("\r\n", notin1) + "\r\n\r\n" +
"Fichiers non présents dans le dossier 2 :\r\n" +
System.String.Join("\r\n", notin2) + "\r\n\r\n" +
"Fichiers différents :\r\n" +
System.String.Join("\r\n", diff) + "\r\n\r\n" +
"Fichiers Identiques :\r\n" +
System.String.Join("\r\n", id);
end: ;
}
private bool Contains(FileInfo[] List, FileInfo Elmt)
{
foreach (FileInfo MyFileInfo in List)
{
if (MyFileInfo.Name == Elmt.Name)
{
return true;
}
}
return false;
}
private string Read(string FileToRead)
{
FileStream OrigFile = new FileStream(FileToRead, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(OrigFile, System.Text.Encoding.Default);
string s = sr.ReadToEnd();
sr.Close();
OrigFile.Close();
return (s);
}
}
}

J'aurais aimé savoir ce que vous pensez de la façon de programmer et comment vous auriez traités ce problème. Quels classes, méthodes, propriétés,... auriez vous utilisés ?

merci d'avance pour vos conseils.
Mathmax

8 réponses

Messages postés
2368
Date d'inscription
mardi 17 avril 2001
Statut
Modérateur
Dernière intervention
26 décembre 2007
20
Pas de variables publiques => variable privé + accesseur

 public ArrayList PasDans1 = new ArrayList();
        public ArrayList PasDans2 = new ArrayList();
        public ArrayList Differents = new ArrayList();
        public ArrayList Identiques = new ArrayList();

-------------------------------------
Pas de textBox3.Text = "";
mais du string.Empty si besoin.
-------------------------------------
Pas de try catch général, il faut catcher une exception spécifique.
Ex : catch (DirectoryNotFoundException dnfe)

    try { Folder2 = new DirectoryInfo(textBox2.Text); }
            catch
-------------------------------------
 if (!Contains(MyFileInfoList1, MyFileInfo))
                    PasDans1.Add(MyFileInfo.Name);                 

Utilises une hastable pour ce genre de stockage.
-------------------------------------
             goto end;

Forbidden words !!!!

Utilise un "Return" ou mieux throw une exception personnalisée.
-------------------------------------
button1_Click

nomme correctement tes variables pour qu'elles soient humainement compréhensible.
-------------------------------------
textBox3.Text = "Fichiers non présents dans le dossier 1 :\r\n" +
                System.String.Join("\r\n", notin1) + "\r\n\r\n" +
                "Fichiers non présents dans le dossier 2 :\r\n" +
                System.String.Join("\r\n", notin2) + "\r\n\r\n" +
                "Fichiers différents :\r\n" +
                System.String.Join("\r\n", diff) + "\r\n\r\n" +
                "Fichiers Identiques :\r\n" +
                System.String.Join("\r\n", id);
Burk :
Utilise un System.Text.StringBuilder et les fonction Append / AppendFormat

Essaie de ne pas mettre de chaîne en dur, passe par des constantes ou des ressources si tu veux localiser.

Pas de \r\n mais Environment.NewLine.
---------------------------------------

...
::|The S@ib|:: MVP C#.NET
Messages postés
403
Date d'inscription
vendredi 28 octobre 2005
Statut
Membre
Dernière intervention
31 août 2008

Merci beaucoup pour tes conseils TheSaib. J'ai revu le code de mon programme. Celà donne :

namespace Compare
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void BtnStart_Click(object sender, EventArgs e)
        {
            textBox3.Text = string.Empty;
            DirectoryInfo Folder1, Folder2;
            int numero = 1;
            try
            {
                Folder1 = new DirectoryInfo(textBox1.Text);
                numero++;
                Folder2 = new DirectoryInfo(textBox2.Text);
            }
            catch
            {
                MessageBox.Show("Chemin " + numero + " incorrect !", "ATTENTION !", MessageBoxButtons.OK);
                return;
            }
            FileInfo[] MyFileInfoList1 = Folder1.GetFiles();
            FileInfo[] MyFileInfoList2 = Folder2.GetFiles();

            Hashtable DicoFiles = new Hashtable();

            foreach (FileInfo MyFileInfo in MyFileInfoList1)
                DicoFiles.Add(MyFileInfo.Name, 1);

            foreach (FileInfo MyFileInfo in MyFileInfoList2)
            {
                if (DicoFiles.ContainsKey(MyFileInfo.Name))
                    if (Read(textBox1.Text + "\" + MyFileInfo.Name) == Read(textBox2.Text + "\" + MyFileInfo.Name))
                        DicoFiles[MyFileInfo.Name] = 3;
                    else
                        DicoFiles[MyFileInfo.Name] = 2;
                else
                    DicoFiles.Add(MyFileInfo.Name, 0);
            }
           
            string[] Catégories = {"Fichiers non présents dans le dossier 1 :",
                "Fichiers non présents dans le dossier 2 :",
                "Fichiers différents mais portant le même nom :",
                "Fichiers identiques :"};

            IEnumerator clés = DicoFiles.Keys.GetEnumerator();

            while(clés.MoveNext())
                Catégories[(int)DicoFiles[clés.Current]] += Environment.NewLine + clés.Current;

            textBox3.Text = System.String.Join(Environment.NewLine + Environment.NewLine,Catégories);
        }
        private string Read(string FileToRead)
        {
            FileStream OrigFile = new FileStream(FileToRead, FileMode.Open, FileAccess.Read);
            StreamReader sr = new StreamReader(OrigFile, System.Text.Encoding.Default);
            string s = sr.ReadToEnd();
            sr.Close();
            OrigFile.Close();
            return (s);
        }
    }
}

Plusieurs questions concernant tes remarques :

j'ai essayé de catcher une exeption spécifique mais je n'ai pas trouvé celle qui convenait. catcher avec DirectoryNotFoundException ne marche pas, quand je débug le programme il me dit que le path n'est pas valide. Comment trouver le nom de l'exeption ?

pourquoi vaut-il mieux mettre string.empty que "" et Environment.NewLine que "\r\n" ?

je n'ai pas trop compris l'utilité d'utiliser un streambuilder (du moins dans mon nouveau code).

sinon peut-on dire que ce programme est correctement programmé objet ?

J'ai cependant encore du mal à voir ce qui différencie sur le fond la programmation objet de la programmation procédurale...

Mathmax
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
54
Salut,
En ce qui concerne l'exception, il s'agit sûrement d'une ArgumentException (si le path à des charactères invalides)
Sinon, il parle d'un StringBuilder et pas d'un StreamBuilder (qui je crois n'existe pas).
Un StringBuilder à le gros avantage d'être beaucoup plus rapide et de pouvoir directement formatter les données.

<hr size= "2" /> VC# forever
=13319
Messages postés
403
Date d'inscription
vendredi 28 octobre 2005
Statut
Membre
Dernière intervention
31 août 2008

J'ai refais le programme en utilisant cette fois ci une classe. Crois-tu que c'était justifié ? Si oui, est elle bien conçue ?

Voici le code de la classe :

namespace Compare
{
    class Comparaison
    {
        //attributs
        private string _path1, _path2;
        private Hashtable _DicoFiles;
        private string[] _Catégories = new string[4];

        //constructeur
        public Comparaison(string P1, string P2)
        {
            DirectoryInfo Folder1, Folder2;
            int numero = 1;
            try
            {
                Folder1 = new DirectoryInfo(P1);
                numero++;
                Folder2 = new DirectoryInfo(P2);
            }
            catch
            {
                MessageBox.Show("Chemin " + numero + " incorrect !", "ATTENTION !", MessageBoxButtons.OK);
                return;
            }

            FileInfo[] MyFileInfoList1 = Folder1.GetFiles();
            FileInfo[] MyFileInfoList2 = Folder2.GetFiles();

            Hashtable DICOFILES = new Hashtable();
            foreach (FileInfo MyFileInfo in MyFileInfoList1)
                DICOFILES.Add(MyFileInfo.Name, 1);

            foreach (FileInfo MyFileInfo in MyFileInfoList2)
            {
                if (DICOFILES.ContainsKey(MyFileInfo.Name))
                    if (Read(P1 + "\" + MyFileInfo.Name) == Read(P2 + "\" + MyFileInfo.Name))
                        DICOFILES[MyFileInfo.Name] = 3;
                    else
                        DICOFILES[MyFileInfo.Name] = 2;
                else
                    DICOFILES.Add(MyFileInfo.Name, 0);
            }

            IEnumerator clés = DICOFILES.Keys.GetEnumerator();

            while (clés.MoveNext())
                this._Catégories[(int)DICOFILES[clés.Current]] += clés.Current + Environment.NewLine;

            this._DicoFiles = DICOFILES;
            this._path1 = P1;
            this._path2 = P2;
        }

        //propriétés
        public Hashtable DicoFiles{
            get { return _DicoFiles; }
            set { _DicoFiles=value; }
        }
        public string path1{
            get { return _path1; }
            set { _path1=value; }
        }
        public string path2{
            get { return _path2; }
            set { _path2=value; }
        }
        public string PasDans1{
            get { return _Catégories[0]; }
        }
        public string PasDans2{
            get { return _Catégories[1]; }
        }
        public string Différents{
            get { return _Catégories[2]; }
        }
        public string Identiques{
            get { return _Catégories[3]; }
        }

        private string Read(string FileToRead)
        {
            FileStream OrigFile = new FileStream(FileToRead, FileMode.Open, FileAccess.Read);
            StreamReader sr = new StreamReader(OrigFile, System.Text.Encoding.Default);
            string s = sr.ReadToEnd();
            sr.Close();
            OrigFile.Close();
            return (s);
        }
    }
}

et le code du programme :

namespace Compare
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void radioButton1_Click(object sender, EventArgs e)
        {
            Comparaison ObjComparaison = new Comparaison(textBox1.Text, textBox2.Text);
            textBox3.Text = ObjComparaison.PasDans1;
        }

        private void radioButton2_Click(object sender, EventArgs e)
        {
            Comparaison ObjComparaison = new Comparaison(textBox1.Text, textBox2.Text);
            textBox3.Text = ObjComparaison.PasDans2;
        }

        private void radioButton3_Click(object sender, EventArgs e)
        {
            Comparaison ObjComparaison = new Comparaison(textBox1.Text, textBox2.Text);
            textBox3.Text = ObjComparaison.Différents;
        }

        private void radioButton4_Click(object sender, EventArgs e)
        {
            Comparaison ObjComparaison = new Comparaison(textBox1.Text, textBox2.Text);
            textBox3.Text = ObjComparaison.Identiques;
        }
    }
}

Ce que je n'arrive pas encore trop à cerner c'est l'utilité d'utiliser une classe.

Peut être préfères tu avoir le projet entier sous les yeux ? Tu peux le télécharger ici : http://www.orkos.com/tests/Compare.zip

En éspèrant ne pas être trop dérangeant,
Mathmax
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
54
Salut,
Voilà quelques idées :

1) Spécifié l'accessiblité de la class : public (par exemple).
2) Initialisé les variables avant leur utilisation.
3) Oublier l'accentuation pour nommer les variables !!!!!!
4) Autre remarque : en .NET, la convention veut que les variables commence par une miniscule (donc plutôt "Pascal notation" que "Camel notation"), pas d'accentuation, en toute lettre juste pour les constantes, etc.

Pour vraiment comprendre ce que c'est qu'une classe, une interface, une classe abstraite, etc, il faut pas mal de pratique, surtout quand on vient d'un monde comme le C où toutes ces notions n'existent pas...

<hr size= "2" /> VC# forever
=13319
Messages postés
403
Date d'inscription
vendredi 28 octobre 2005
Statut
Membre
Dernière intervention
31 août 2008

Ok merci pour ces précisions. J'éspère que comme tu le dis la pratique me fera mieux comprendre quand il faut utiliser des classes et comment les construire. Sans vouloir insister, j'aurais juste encore une petite question : Y avait-il un avantage à utiliser une classe comparaison ici ou mon programme était plus propre sans cette classe ?

Mathmax
Messages postés
5487
Date d'inscription
dimanche 4 août 2002
Statut
Modérateur
Dernière intervention
20 juin 2013
54
Oui, il faut une Class, c'est évident ! (tu ne peux pas laisser des milliers de lignes code dans une Form : une Form devrait plutôt appeler la fonctionnalité que tu implémentes ailleurs).
Quand à dire si elle est bien construite... je te cacherai pas que je n'aurais pas fait comme ça!
Je ne peux pas t'expliquer en détail pourquoi, y'a certain truc qui vienne avec le feeling et l'expérience mais encore quelques conseils :

Le constructeur, dans la plupart des cas, ne fait rien si ce n'est initialiser des variables, appeler un constructeur du parent, ou des trucs basique comme ça. La raison à ceci est simple : quand tu vas construire ton object, tu vas passer dans le constructeur, puis tu vas appeler certaine méthode de ton object, mais généralement tu vas utiliser seulement une part de la fonctionnalité qu'il t'offre. Alors, à quoi ça sert de faire des milliers de calculs et je ne sais pas tout quoi, si c'est pour juste appeler une méthode ?

Une idée pour pouvoir poser l'architecture d'une class, c'est de se dire : "Comment je voudrais l'utiliser?", puis ensuite, créer la class en fonction de ceci (cette méthode à ses désavantage aussi). Petite exemple pour illustrer la méthode. Dans ton cas, ce qui serait bien c'est de faire (ceci est exemple) :

FileComparer fComparer = new FileComparer(string path1, string path2);
bool isSame = fComparer.Comparer(bool caseSensitiv);

Donc ensuite tu constuis ta class en fonction de ceci (c'est un exemple extrement simple et peut-êtrs pas très réaliste, parce qu'il faut encore implémenté des autres fonctions).
Le meilleur conseil : pratiquer, pratiquer et pratiquer. Codes-sources te sera de grande aide pour poser des questions, et trouver des codes qui te permettront d'apprendre plus

<hr size="2" /> VC# forever
=13319
Messages postés
403
Date d'inscription
vendredi 28 octobre 2005
Statut
Membre
Dernière intervention
31 août 2008

merci encore pour ces conseils Bidou. En fait j'avais fabriqué le dictionnaire Hashtable dans le constructeur parce que sinon j'aurais dû le reconstruire dans chaque propriété (pasdans1, pasdans2, différents, identiques). Ce qui fait que quand j'aurais construit un objet, à chaque fois que j'aurais utilisé une nouvelle propriété, un nouveau dictionnaire aurait été construit. J'ai donc préférer construire ce dictionnaire une bonne fois pour toute dès la construction de l'objet. J'en ai donc fais une propriété, que j'initialise avec le constructeur. Mais peut-être y avait-il une autre méthode ?

Mathmax