Laden...

Lieber Objekte erstellen oder Methoden direkt ohne Objekt aufrufen?

Erstellt von lschwarz vor 5 Jahren Letzter Beitrag vor 5 Jahren 920 Views
L
lschwarz Themenstarter:in
6 Beiträge seit 2018
vor 5 Jahren
Lieber Objekte erstellen oder Methoden direkt ohne Objekt aufrufen?

Hallo zusammen,

da ich mir im Moment die Programmierung beibringe habe ich mir ein kleines Projekt erstellt. Es ist eine WPF-Anwendung, bei der der Nutzer die Geschwindigkeit eines Körpers und den Radius einer Kurve eingibt. Darauf gibt die Anwendung die Zentrifugalbeschleunigung, den G-Faktor und nach Eingabe der Masse des Körpers auch die Zentrifugalkraft aus. Ich habe dies alles in einer Klasse gemacht. Dies ist der Code:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;


namespace FliehkraftRechner
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        const double gravity = 9.81;

        public MainWindow()
        {
            InitializeComponent();

            SetCursor(TextBoxGeschwindigkeit);
        }

        private void ButtonCalc_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                double zentrBeschl = CalcZentrBeschl(double.Parse(TextBoxGeschwindigkeit.Text), double.Parse(TextBoxRadius.Text));
                double gFactor = CalcGFactor(zentrBeschl);
                if (!String.IsNullOrEmpty(TextBoxMasse.Text))
                {
                    double zentrKraft = CalcZentrKraft(zentrBeschl, double.Parse(TextBoxMasse.Text));
                    PrintErgebnis(zentrBeschl, gFactor, zentrKraft);
                }
                else
                {
                    PrintErgebnis(zentrBeschl, gFactor);
                }
            }
            catch
            {
                MessageBox.Show("Fehler. Bitte einen gültigen Wert eingeben.");

                TextBoxGeschwindigkeit.Text = "";
                TextBoxRadius.Text = "";
                TextBoxMasse.Text = "";
            }
            finally
            {
                SetCursor(TextBoxGeschwindigkeit);
            }

        }

        // Setzt den Cursor in Ursprungszustand / in TextBox
        public void SetCursor (System.Windows.IInputElement element)
        {
            element.Focus();
        }

        // Berechnet Zentrifugalbeschleunigung und gibt Wert als [m/(s^2)]zurück
        public double CalcZentrBeschl (double v, double r)
        {
            double zentrBeschl = 0.00;
            
            if (v > 0 && r > 0)
            {
                zentrBeschl = (v * v) / r;
                return zentrBeschl;
            }
            else
            {
                return 0.00;
            }
        }

        // Berechnet G-Faktor
        public double CalcGFactor (double zentrBeschl)
        {
            return zentrBeschl / gravity;
        }

        public double CalcZentrKraft (double zentrBeschl, double masse)
        {
            if (masse > 0)
            {
                return zentrBeschl * masse;
            }
            else
            {
                return 0.00;
            }
        }

        // Zeigt die Daten von Zentrifugalbeschl und G-Faktor auf dem Ergebnisfeld an. Überladene Methode
        public void PrintErgebnis (double zentrBeschl, double gFactor)
        {
            TextBoxErgebnisse.Text = $"Zentr.-Beschl.: {zentrBeschl.ToString("0.00")}m/(s^2)\n" +
            $"g-Faktor: {gFactor.ToString("0.00")}G\n";
        }

        public void PrintErgebnis (double zentrBeschl, double gFactor, double zentrKraft)
        {
            TextBoxErgebnisse.Text = $"Zentr.-Beschl.: {zentrBeschl.ToString("0.00")}m/(s^2)\n" +
            $"g-Faktor: {gFactor.ToString("0.00")}G\n" +
            $"Zentr.-Kraft: {zentrKraft.ToString("0.00")}N";
        }
    }
}


Nun habe ich zwei Fragen, welche sich mir als Anfänger nicht erschließen.

  1. Ich habe alle Methoden in die selbe Klasse gepackt. Sobald ich aber das Keyword **static ** zu einer Methode packe, wird mir z.B. eine TextBox rot unterstrichen mit dem Hinweis "Für das nicht statische Feld, die Methode oder die Eigenschaft "MainWindow.TextBoxErgebnisse" ist ein Objektverweis erforderlich. Eigentlich ist es doch so, dass wenn ich keine Instanz einer Klasse erstelle, sondern hier in diesem Falle ja nur eine Berechnung durchführen möchte, die Methode ohne Instanz aufrufen möchte. Oder liegt es daran, dass ich eine Instanz des MainWindows habe?

  2. Wäre es in diesem Falle geschickter, eine Klasse "Berechnung" zu schreiben, welche die ganten Methoden und auch Felder besitzt und dann bei jedem Klick auf den Berechnen-Button eine neue Instanz erstelle. Ich weiß oft noch nicht so ganz, wann ich Dinge lieber in einer Klasse schreiben soll (nach OOP-Mustern) oder wann es sinnvoller ist, das ganze einfach "normal" ohne Klassen und Instanzen zu machen.

Vielen Dank und viele Grüße

16.835 Beiträge seit 2008
vor 5 Jahren

Business Logik sollte niemals eine Referenz auf die UI haben, egal ob Textbox oder Window.
[Artikel] Drei-Schichten-Architektur

Daher ist damit auch Frage zwei beantwortet 😉
> Natürlich ist es besser das auszulagern.

2.079 Beiträge seit 2012
vor 5 Jahren

Frage 1:

Das liegt daran, dass Du in der Methode Member verwendest, die nur für eine Instanz verfügbar sind, wie eben die TextBox. Statisch geht das hier auch einfach nicht, da alle Inhalte einer View Instanz-Member sind, macht auch Sinn, immerhin willst Du bei Bedarf die selbe View mehrfach haben können. Bei einem Window macht das auf den ersten Blick noch wenig Sinn, aber sobald Du UserControls baust, bist Du froh, dass es so ist.

Frage 2:

Der Grundgedanke ist schon mal gut. Es lohnt sich, seinen Code zu strukturieren und Dinge, die funktional zusammen gehören, in eine Klasse auszulagern, allerdings kannst Du damit auch viel falsch machen. Eine eigene Klasse sollte keine zu harten Abhängigkeiten zu anderen Klassen haben. Das kann man nicht immer vermeiden, aber wo man es vermeiden kann, sollte man das auch tun. Bei einer View würde ich sagen, dass z.B. die TextBox keine notwendige Abhängigkeit ist, die solltest Du vermeiden.

Im Falle von reinen mathematischen Berechnungen würde ich mich an der Math-Klasse orientieren. Eine statische Klasse mit Berechnungs-Methoden, die keinen Zustand brauchen und alles Nötige als Methoden-Parameter übergeben bekommen. KEINE View-Elemente, in den meisten Fällen dürften das nur Zahlen sein.
Die Nachteile von statischen Klassen sind hier mMn. irrelevant, da die Methoden keinen eigenen Status benötigen, sie können immer, jederzeit und aus jedem Thread heraus aufgerufen werden, ohne dass es Probleme gibt.

Im Falle einer View würde ich persönlich nie irgendwelche View-Elemente nach außen geben bzw. andere Klassen davon abhängig machen, egal worum es geht. Die einzelnen View-Inhalte gehören der View, wenn ich z.B. den Text einer TextBox nach außen bekannt machen will, dann bekommt meine View dafür eine eigene Property oder der Text ist ein Parameter für die andere Klasse/Methode.

Allgemein lohnt sich bei WPF aber der Blick auf MVVM.
Bei kleinen Projekten mag das auf den ersten Blick etwas merkwürdig klingen, aber wenn Du die Zusammenhänge verstanden und ein bisschen Übung damit hast, geht das leicht von der Hand. Bei WPF kannst Du dann nämlich auch von einigen sehr praktischen Features profitieren, wie z.B. das hervorragende Binding-System.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.