Betrachten Sie diese Methoden:
public List<Employee> GetAllEmployees()
{
using (Entities entities = new Entities())
{
return entities.Employees.ToList();
}
}
public List<Job> GetAllJobs()
{
using (Entities entities = new Entities())
{
return entities.Jobs.ToList();
}
}
public List<Task> GetAllTasksOfTheJob(Job job)
{
using (Entities entities = new Entities())
{
return entities.Tasks.Where(t => t.JobId == job.Id).ToList();
}
}
Die Verwendung von Block ist dieselbe und wurde hier dreimal wiederholt (natürlich mehr als 100 Mal in der realen Anwendung). Wie ist es möglich, ein DRY-Prinzip (Don't Repeat Yourself) für einen using
Block zu implementieren ? Gilt es überhaupt als Verstoß gegen den DRY-Grundsatz?
Update: Ich spreche nicht darüber, was innerhalb des using
Blocks implementiert wurde . Was ich hier eigentlich meine, ist das using (Entities entities = new Entities())
. Diese Zeile wird mindestens 100 Mal wiederholt.
c#
design-patterns
dry
Saeed Neamati
quelle
quelle
Antworten:
Eine Idee wäre, es mit einer Funktion zu umschließen, die a annimmt
Func
.Etwas wie das
Dann wird Ihr oben genannter Code
Ich habe auch
Entities
einen Typ-Parameter erstellt, da ich davon ausgehe, dass Sie mehr als einen Typ haben, mit dem Sie dies tun. Wenn dies nicht der Fall ist, können Sie es entfernen und einfach den type-Parameter für den Rückgabetyp verwenden.Um ehrlich zu sein, trägt diese Art von Code überhaupt nicht zur Lesbarkeit bei. Nach meiner Erfahrung haben es auch die mehr Jr.-Mitarbeiter sehr schwer.
Aktualisieren Sie einige zusätzliche Variationen der Helfer, die Sie möglicherweise in Betracht ziehen
quelle
Entities
.IEnumerable
die Funktion für den Fall, dass es irgendwelche Nicht-IEnumerable
Eigenschaften von T gab, die der Aufrufer zurückgeben wollte, weggelassen, aber Sie haben Recht, es würde ein bisschen aufräumen. VielleichtIEnumerable
wäre es gut , einen Helfer für Single und Ergebnisse zu haben. Das heißt, ich denke immer noch, es verlangsamt die Erkennung dessen, was der Code tut, insbesondere für jemanden, der nicht daran gewöhnt ist, viele Generika und Lambdas zu verwenden (z. B. Ihre Kollegen, die NICHT auf SO sind :))WithEntities
, verwenden SieFunc<T,IEnumerable<K>>
anstelle vonFunc<T,K>
und geben Sie "WithEntities" einen besseren Namen (wie SelectEntities). Und ich denke nicht, dass "Entities" hier ein generischer Parameter sein muss.where T : IDisposable, new()
, wieusing
esIDisposable
für die Arbeit erforderlich ist .Für mich wäre das, als würde man sich mehrmals darum sorgen, dieselbe Sammlung zu durchsuchen: Es ist nur etwas, was man tun muss. Jeder Versuch, ihn weiter zu abstrahieren, würde den Code viel weniger lesbar machen.
quelle
foreach
Objekt über einer sehr großen Sammlung befindet oder die Logik innerhalb derforeach
Schleife beispielsweise zeitaufwändig ist. Ein Motto, das ich übernommen habe: Sei nicht besessen, sondern achte immer auf deine VorgehensweiseEs hört sich so an, als würden Sie das Prinzip "Einmal und nur einmal" mit dem DRY-Prinzip verwechseln. Das DRY-Prinzip lautet:
Das Prinzip von einmal und nur einmal unterscheidet sich jedoch geringfügig.
Das DRY-Prinzip wird normalerweise im Kontext der eigentlichen Logik verwendet, nicht so sehr redundant mit Anweisungen:
Quelle
quelle
Ich kann die Verwendung von
using
hier nicht erkennen:Wie wäre es mit:
Oder noch besser, da ich nicht denke, dass Sie jedes Mal ein neues Objekt erstellen müssen.
Was die Verletzung von DRY angeht: DRY gilt auf dieser Ebene nicht. Tatsächlich gibt es kein Prinzip, außer dem der Lesbarkeit. Der Versuch, DRY auf dieser Ebene anzuwenden, ist eigentlich nur eine architektonische Mikrooptimierung, die wie jede Mikrooptimierung nur einen Fahrradabwurf darstellt und keine Probleme löst, sondern sogar das Risiko birgt, neue einzuführen.
Aus eigener Erfahrung weiß ich, dass Sie, wenn Sie versuchen, die Code-Redundanz auf dieser Ebene zu verringern, die Code-Qualität negativ beeinflussen, indem Sie das verschleiern, was wirklich klar und einfach war.
Bearbeiten:
Ok. Das Problem ist also nicht die using-Anweisung, sondern die Abhängigkeit von dem Objekt, das Sie jedes Mal erstellen. Ich würde vorschlagen, einen Konstruktor zu injizieren:
quelle
using (CustomTransaction transaction = new CustomTransaction())
in unserem Code verwenden, um den Umfang einer Transaktion zu definieren. Das kann nicht zu einem einzigen Objekt gebündelt werden, und Sie sollten an jeder Stelle, an der Sie eine Transaktion verwenden möchten, einen Block schreiben. Was nun , wenn Sie von der Art der Transaktion zu ändern ,CustomTransaction
umBuiltInTransaction
in mehr als 500 Methoden? Dies scheint mir eine sich wiederholende Aufgabe und ein Verstoß gegen das DRY-Prinzip zu sein.using
(in diesem Zusammenhang) noch eine unbekannte "bequeme Syntax"? Warum ist es so schön zu benutzen =)Es wird nicht nur doppelter Code verwendet (übrigens ist es doppelter Code und wird tatsächlich mit einer try..catch..finally-Anweisung verglichen), sondern auch die toList. Ich würde Ihren Code so umgestalten:
quelle
Da es hier außer der letzten keine Geschäftslogik gibt. Meiner Meinung nach ist es nicht wirklich trocken.
Der letzte hat kein DRY im using-Block, aber ich denke, die where-Klausel sollte sich ändern, wo immer sie verwendet wird.
Dies ist ein typischer Job für Codegeneratoren. Schreiben und decken Sie den Codegenerator ab und lassen Sie ihn für jeden Typ generieren.
quelle
using (Entities entities = new Entities())
Block. Ich meine, diese Codezeile wird 100 Mal wiederholt und wird immer mehr wiederholt.Da Sie immer wieder dasselbe Einwegobjekt erstellen und zerstören, ist Ihre Klasse selbst ein guter Kandidat für die Implementierung des IDisposable-Musters.
Das bedeutet, dass Sie nur das "using" benötigen, wenn Sie eine Instanz Ihrer Klasse erstellen. Wenn Sie nicht möchten, dass die Klasse für die Disposition der Objekte verantwortlich ist, können Sie die Methoden veranlassen, die Abhängigkeit als Argument zu akzeptieren:
quelle
Mein Lieblingsteil der unverständlichen Magie!
Wrap
existiert nur, um das heraus zu abstrahieren oder welche Magie auch immer du brauchst. Ich bin mir nicht sicher, ob ich das die ganze Zeit empfehlen würde, aber es ist möglich es zu benutzen. Die "bessere" Idee wäre, einen DI-Container wie StructureMap zu verwenden und die Entities-Klasse einfach auf den Anforderungskontext zu beschränken, in den Controller einzuspeisen und dann den Lebenszyklus zu erledigen, ohne dass der Controller dies tun muss.quelle
Func
genug, was ich sollte.