Author Archiv

Obwohl wir Jeff Atwood schon vor Monaten von unseren Bloggerliste gestrichen haben, ich habe ihn noch nicht aus meinem Googlereader entfernt. Er ist ein Typ, der liebt viel Wirbel zu verursachen (Scott Hanselmann nennt ihn aus den Wörtern friend und enemy “my friendemy” ;) ) aber manchmal trifft er den Nagel auf den Kopf. So auch in seinem letzten Artikel “The Non-Programming Programmer”.


I wrote that article in 2007, and I am stunned, but not entirely surprised, to hear that three years later “the vast majority” of so-called programmers who apply for a programming job interview are unable to write the smallest of programs. To be clear, hard is a relative term — we’re not talking about complicated, Google-style graduate computer science interview problems. This is extremely simple stuff we’re asking candidates to do. And they can’t. It’s the equivalent of attempting to hire a truck driver and finding out that 90 percent of the job applicants can’t find the gas pedal or the gear shift.

Aus den Unmengen von Kommentaren zu urteilen, ist das ein Problem, mit dem sehr viele zu kämpfen haben. Sein Artikel beschäftigt sich mit der Arbeit von Personen, die Einstellungsgespräche führen, und er versucht ein paar Tipps zu geben, wie man dieses Verfahren führen soll.

Ich musste allerdings beim Lesen ständig auf die Situation “danach” denken, wenn diese NPPs (sprich Non- Programming Programmers) bereits aus irgendeinem Grund eingestellt wurden. Aus Erfahrung weiß ich genau, was mir viel über jemanden verraten würde: weiß er/sie etwas über Clean Code, kennt er die Folgen vom Creepy Code und wie ist die persönliche Einstellung dazu? Also zusammengefasst: geht es um einen professionellen Softwareentwickler? Dafür würden auch ein Code-Review oder ein paar Fragen reichen.

Was haltet ihr von Coding bei der Einstellung? Ist das auch in Europa üblich? Würde das die unpassenden Personen ausfiltern? Oder soll man sich die Zeit nehmen und dem Kandidaten “ungesehen” die Probezeit anbieten? Würde das vielleicht zu viele Personen ausfiltern? Kann man sich das überhaupt erlauben in der heutigen Zeit der Fachkräftemangel? (Gut, man fragt sich natürlich, ob wir hier über “Fachkraft” sprechen dürfen…)

kick it on dotnet-kicks.de

Share

Comments 14 Kommentare »

Wir haben wie viele von uns in der Webentwicklung vor vielen Jahren mit Scriptsprachen und mit prozeduralen – Spaghetti-Code ;) – angefangen. Mit der Zeit wuchs unsere Webpräsenz zu einer unüberschaubaren Anwendung mit manchen Seiten, die keiner von uns mehr anfassen wollte – aus Angst vor den Konsequenzen.

Um etwas Ordnung in die Webanwendungen zu bringen, haben wir also vor 4 Jahren angefangen, nach einem 3-Schichten-Modell zu entwickeln. Wir haben neue Funktionalitäten und neue Anwendungen nur noch so gebaut und wir waren für eine kurze Zeit zufrieden. Alles lief gut. Als wir den Umstieg auf .NET begonnen haben, haben wir weiterhin nach einem Mehr-Schichten-Modell gearbeitet, wir haben nur die Anzahl der Schichten erhöht.

Die Anwendungen wuchsen weiter, wir haben immer mehr Bereiche ausgelagert und diese hauptsächlich mit Webservices angesprochen. Währenddessen waren wir daran, unser Hauptprodukt, ein Portal für unseren Kunden mit der neuen Technologie entsprechend der alten Anforderung neu zu bauen. Und dann ist es passiert: ehe wir uns versahen, hatten wir ein riesiges Projekt, das alle mögliche Anwendungen eingebunden bzw. durch Webservices angesprochen hat. Die Grenzen waren fließend, eventuelle Änderungen an anderen Anwendungen konnten das Projekt unbuildbar machen, also ein ähnlicher Zustand wie vor paar Jahren zuvor.

Die Weiterentwicklung hat nicht nur in unserer Art zu Programmieren stattgefunden, wir selbst haben uns auch weiterentwickelt, wir haben die Community kennen gelernt. Bei den Open Space-Veranstaltungen haben wir Stefan und Ralf kennengelernt und durch sie eine andere Sichtweise der Dinge: die Modellierung einer Lösung durch Softwarezellen.

Wir haben sie zu uns eingeladen und uns die Idee erklären lassen. Das Stichwort heißt Holon. Wikipedia definiert ein Holon folgendermaßen:

Der Begriff Holon (von griech. ὅλος, hólos und ὀν, on „ganzes Seiendes“) wurde von Arthur Koestler geprägt und bedeutet ein Ganzes, das Teil eines anderen Ganzen ist. Es wird auch als “Ganzes/Teil” umschrieben.

Jede Anwendung ist ein Ganzes, die aus Teilen besteht, die ihrerseits auch als Ganze zu betrachten sind.

Seit dem Besuch von Ralf und Stefan haben wir uns die Artikelserie von Ralf von dotnetpro durchgelesen, die Webcasts (Teil 1, Teil 2) angeschaut und wir haben angefangen, diese Modellierung auszuprobieren.

Wir haben noch einen langen und interessanten Weg vor uns, aber eins ist jetzt schon sicher: wir werden versuchen unsere nächste Projekte durch Softzellen modellieren.

Solange die Komplexität nicht wieder die Überhand gewinnt ;)

kick it on dotnet-kicks.de

Share

Comments 2 Kommentare »

Ich lese gerade das Buch Agile Principles, Patterns and Practices in C# from Robert C. Martin und Micah Martin. Dabei bin ich auf eine so tolle Metapher gestoßen, dass ich sie hier unbedingt niederschreiben wollte (die Übersetzung ist frei von mir):

Refaktorisierung ist, wie die Küche nach dem Abendessen aufzuräumen. Das erste Mal, wenn du das überspringst, bist mit der Kochen schneller fertig. Aber das Fehlen von sauberem Geschirr und sauberem Arbeitsplatz führt dazu, dass die Vorbereitung diesmal länger dauert. Am liebsten würdest du das Aufräumen wieder überspringen. Tatsache ist, dass du heute ohne das lästige Geschirrspülen wieder schneller fertig wärst. Aber der Schmutz wächst und wächst. Jedes Mal dauert es länger, das richtige Küchengerät zu finden, die vertrockneten Schmutzschichten vom Geschirr ab zu kratzen, und so weiter und so fort. Das Vorbereiten des Abendessens dauert ewig…
Das Überspringen der Abwasch macht das Kochen eigentlich nicht wirklich schneller.

Das Ziel von Refaktorisierung ist die Bereinigung des Codes, jeden Tag, jede Stunde, jede Minute. Wir wollen nicht den Code versauen. Wir wollen nicht ständig gezwungen zu sein, schmutzigen Bits und Bytes sauber zu schrubben. Wir wollen jeder Zeit in der Lage zu sein, unser System mühelos zu erweitern und zu verändern. Der wichtigste Faktor, der uns das möglich macht, ist die Sauberkeit des Codes.

Das Buch habe zwar noch nicht ganz durch, aber was ich bis jetzt gelesen habe, kann ich nur unterschreiben. Es beinhaltet die Prinzipien, wie man in einem agilen Team sauberen Code plant und Mustern und Beispiele, wie man ihn schreibt. Und wie ihr ja sieht, ist es in eine ganz tolle Art geschrieben :)

kick it on dotnet-kicks.de

Share

Comments Keine Kommentare »

Der vorherige DbC-Artikel ist ziemlich “abstrakt” ausgefallen, es haben einfach Beispiele gefehlt. Das möchte ich hiermit nachholen.

Erstens muss man die IDE anpassen: im März kommt .NET 4.0 raus und da wird Design by Contract mitgeliefert. Man kann das Konzept aber jetzt schon anwenden, wenn man die Assembly zusätzlich installiert. Danach muss man die dll referenzieren und im Eigenschaftenfenster des Projektes im neuen Tab Code Contracts das Runtime Checking einstellen.

Jetzt zum Code: Nehmen wir eine ganz einfache Klasse Bill deren Objekte mit einem IRepository gespeichert bzw. geladen werden.

    1 using System.Diagnostics.Contracts;

    2 namespace ContractsPrototyp

    3 {

    4     public class Bill

    5     {

    6         public int Id { get; set; }

    7         public string Number { get; set; }

    8         public double Value { get; set; }

    9     }

   10 

   11

   12     public interface IRepository

   13     {

   14         Bill GetBill(string number);

   15         void SaveBill(Bill bill);

   16     }

Die Kontrakte kann man in den einzelnen Methoden oder für eine ganze Klasse schreiben (unter dem Attribut ContractInvariantMethode) aber ich finde am schönsten, dass man die auch auslagern kann: durch eine gegenseitige Markierung können Kontrakt-Klassen und Interfaces als “Paare” definiert werden:

   11     [ContractClass(typeof(RepositoryContracts))]

   12     public interface IRepository

   13     {

   14         Bill GetBill(string number);

   15         void SaveBill(Bill bill);

   16     }

   17     [ContractClassFor(typeof(IRepository))]

   18     public class RepositoryContracts:IRepository

   19     {

   20         public Bill GetBill(string number)

   21         {

   22             Contract.Requires(!string.IsNullOrEmpty(number));

   23             return null;

   24         }

   25 

   26         public void SaveBill(Bill bill)

   27         {

   28             Contract.Ensures(bill.Id > 0);

   29         }

   30     }

Eine Vorbedingung wird mit Contract.Requires und eine Nachbedingung mit Contract.Ensures definiert. Beide Methoden bekommen boolische Ausdrücke. Diese Ausdrücke müssen frei von Seiteneffekten sein.

Die eigentliche Implementierung der Klasse schaut dann so aus:

   31     public class Repository:IRepository

   32     {

   33         public Bill GetBill(string nummer)

   34         {

   35             //Würde das Objekt aus Datenhaltung laden

   36             return new Bill();

   37         }

   38 

   39         public void SaveBill(Bill bill)

   40         {

   41             //Würde das Objekt speichern und ihm eine Id zuweisen

   42             if (BillIsValid( bill )) bill.Id++;

   43         }

   44 

   45         private static bool BillIsValid(Bill bill)

   46         {

   47             return !string.IsNullOrEmpty(bill.Nummer);

   48         }

   49     }

Woher können wir wissen, dass das funktioniert? Es ist einfach, wir schreiben ein Paar Tests dazu!
Bei Kontraktverletzung wird eine Exception geworfen. Um diese – und dadurch die genaue Verletzung – überprüfen zu können braucht man etwas Workaround:

   55     [TestFixture]

   56     public class BillTests

   57     {

   58         private IRepository m_repository;

   59         private string m_message;

   60 

   61         [SetUp]

   62         public void Setup()

   63         {

   64             m_repository = new Repository();

   65             m_message = string.Empty;

   66             Contract.ContractFailed += ( sender, e ) =>

   67             {

   68                 e.SetUnwind();

   69                 m_message = e.Message;

   70             };

   71         }

Danach sind die Tests dann einfach:

   73         [Test]

   74         public void Laden_mit_leerer_Nummer_verletzt_Kontrakt()

   75         {

   76 

   77             try

   78             {

   79                 m_repository.GetBill( null );

   80             }

   81             catch

   82             {

   83                 //Nichts

   84             }

   85 

   86             Assert.That( m_message, Is.EqualTo( "Precondition failed: !string.IsNullOrEmpty(number)" ) );

   87         }

   88 

   89         [Test]

   90         public void Speichern_Rechnung_ohne_Nummer_verletzt_Kontrakt()

   91         {

   92 

   93             try

   94             {

   95                 m_repository.SaveBill( new Bill{Value = 25} );

   96             }

   97             catch

   98             {

   99                 //Nichts

  100             }

  101 

  102             Assert.That( m_message, Is.EqualTo( "Postcondition failed: bill.Id > 0" ) );

  103         }

Ich hoffe, das Beispiel ist ausführlich genug, um die Vorteile von DbC zu highlighten. Stefan, vielen dank noch mal für den Artikel, ich habe mich natürlich von dir inspirieren lassen.

kick it on dotnet-kicks.de

Share

Comments 3 Kommentare »

Gestern Abend waren wir alle bei einem super Vortrag von Albert über ASP.MVC v2. Die neue Version soll am 22. März zusammen mit .NET 4.0 rauskommen.
Ich will jetzt nicht über all die Neuigkeiten sprechen, die wir gestern erfahren haben und worüber wir uns jetzt schon freuen müssen, nur über einen speziellen Teil: die Validierung der Daten durch Attribute.

Der Grund ist folgender: wir haben uns vor ca. 1 Jahr ein eigenes Attribut namens ObligatoryFieldAttribut gebaut. Dieses wird über die Properties gesetzt, die wir für die Speicherung als unerheblich markieren möchten. Die Überprüfung erfolgt dann durch Reflection.

Und was haben wir gestern erfahren? In der neue MVC-Version gibt es ein neues Attribut namens Required, das genau das tut!

Ihr könnt euch sicher vorstellen, was wir uns gestern gedacht haben: wir lagen genau richtig und wir waren unserer Zeit voraus! Oder haben unsere Büro-Nachbarn – die eine Microsoft Subsidiary sind – Kameras bei uns installiert ;) ?

Share

Comments Keine Kommentare »

Bald kommt .NET 4.0 raus, zusammen mit einem für C# neuen Konzept: Kontrakte im Code mit Spec# festzulegen.

Wikipedia definiert DbC folgendermaßen:

Design by contract (kurz DbC; englisch Entwurf gemäß Vertrag) oder Programming by Contract ist ein Konzept aus dem Bereich der Softwareentwicklung. Ziel ist das reibungslose Zusammenspiel einzelner Programmmodule durch die Definition formaler „Verträge“ zur Verwendung von Schnittstellen, die über deren statische Definition hinausgehen. Entwickelt und eingeführt wurde es von Bertrand Meyer mit der Entwicklung der Programmiersprache Eiffel.

Stefan Lieser hat über das neue Konzept einen sehr guten Artikel in www.visualstudio1.de geschrieben, mit Begriffsklärung und Anwendungsbeispiele. Kurz zusammengefasst ist die Rede von Folgendem: anstelle, dass man in jeder Methode auf not-null or not-empty usw. prüft, schreibt man die Erwartungen als Code hin. Diese werden von Tools wie z.B. Resharper erkannt und bei fehlerhaften Aufruf der Methode, wird der Entwickler gewarnt. Am besten finde ich, dass man den Kontrakt-Code z.B. für einen Interface in eine separate Klasse schreiben kann und diese wird durch Attribute (ContractClassFor) gefunden. Also wird dadurch der eigentliche Code nicht größer.

Das soll nicht bedeuten, dass der Entwickler der Methode die Verantwortung von sich schiebt ;) , sondern dass er Bedingungen der “Nutzung” offenlegt, Informationen, die bisher nur durch das Anschauen des Codes oder durch mündliche/schriftliche Mitteilung möglich war. Mit Spec# kann man die Intention-Revealing Interfaces ganz genau schreiben: mit veröffentlichten und kompilierten Bedingungen.

kick it on dotnet-kicks.de

Share

Comments 4 Kommentare »

Obwohl die allgemeine Meinung ist, dass es sehr schwierig sei, kann man mit folgendem Workaround Webforms sehr einfach und sehr umfangreich testen. Bedingung 1: als Projekt kein Web Site Project sondern eine Web Application erstellen. Bedingung 2: Business Logic in die entsprechende Schicht auslagern.

Nehmen wir zum Beispiel ein einfaches Formular. Nach Absenden des Formulars sollen die Werte aus den 2 Feldern addiert werden. Wenn man per QueryString einen Parameter multiple übergibt, soll das Ergebnis damit multipliziert werden.

Testable WebForm   Testable WebForm Sent

Und nun zum Quellcode: Die automatisch erstellte .designer.cs muss entfernt werden, was man sowieso tun sollte, da man automatisch erstellten Code – also Code, den keiner außer Microsoft unter Kontrolle hat – vermeiden sollte.
Die Inhalte der .designer.cs – also die Definitionen der Web-Elemente – werden in der Klasse als Public Properties erstellt und instantiiert, um bei Zugriffen wie TextBox.Text keine NullReferenceException zu bekommen.

    1 using System;

    2 using System.Web.UI.HtmlControls;

    3 using System.Web.UI.WebControls;

    4 using framework.Testable.Web.UI;

    5 

    6 namespace TestableWebForm

    7 {

    8     public class DefaultPage : System.Web.UI.Page

    9     {

   10 

   11         #region Controls

   12         public HtmlForm Formular;

   13         public TextBox Value1 = new TextBox();

   14         public TextBox Value2 = new TextBox();

   15         public Label Result = new Label();

   16         public Button Submit = new Button();

   17         #endregion

Um das Verhalten testen zu können, haben wir Adapter für die Klassen System.Web.UI.Page, System.Web.HttpRequest und System.Web.HttpResponse geschrieben, und zwar für die Properties und Methoden die uns vorerst interessieren: z.B. Page.IsPostBack, Page.Request, Response.Redirect(string url, bool endResponse). Bei der Benennung haben wir einfach den Namespace System mit framework.Testable ersetzt und wir haben natürlich zu jedem Testable-Objekt einen Interface erstellt.

    1 

    2 namespace framework.Testable.Web

    3 {

    4     namespace UI

    5     {

    6         public interface IPage

    7         {

    8             bool IsPostBack{ get; }

    9             IHttpRequest Request{ get; set; }

   10             IHttpResponse Response{ get; set; }

   11         }

   12 

   13         public class Page : IPage

   14         {

   15             private readonly System.Web.UI.Page m_page;

   16             private IHttpRequest m_request;

   17             private IHttpResponse m_response;

   18 

   19             public Page( System.Web.UI.Page page )

   20             {

   21                 m_page = page;

   22                 m_request = new HttpRequest( m_page );

   23                 m_response = new HttpResponse( m_page );

   24             }

   25 

   26             public bool IsPostBack

   27             {

   28                 get{ return m_page.IsPostBack; }

   29             }

   30 

   31             public IHttpRequest Request

   32             {

   33                 get{ return m_request; }

   34                 set { m_request = value; }

   35             }

   36 

   37             public IHttpResponse Response

   38             {

   39                 get{ return m_response; }

   40                 set { m_response = value; }

   41             }

   42         }

   43     }

   44 }

Um alle gemockte Objekte setzen zu können, haben wir unserer Page-Klasse auch Setter für Request und Response gegeben. Da man Request.Params nicht setzen kann, d.h. Request.Params[] immer ein NullReferenceException verursachen würde, haben wir das Auslesen der Request-Parameter in eine Methode Request.GetParamValue(string name) ausgelagert.

Das war ungefähr alles: in der Seite nutzt man dann anstelle der eigenen Request und Response-Objekten die Testable-Objekte.

    8     public class DefaultPage : System.Web.UI.Page

    9     {

   …

   19         private IPage m_page;

   20         public void SetTestableObjects( IPage page )

   21         {

   22             m_page = page;

   23         }

   24 

   25         public void Page_Load( object sender, EventArgs e )

   26         {

   27             if (m_page == null) m_page = new Page( this );

   28             int multiple = 1;

   29             if (!string.IsNullOrEmpty( m_page.Request.GetParamValue( "multiple" ) )) multiple = Convert.ToInt32( m_page.Request.GetParamValue( "multiple" ) );

   30             if (m_page.IsPostBack)

   31             {

   32                 Result.Text = ( (Convert.ToInt32( Value1.Text ) + Convert.ToInt32( Value2.Text ))*multiple ).ToString();

   33             }

   34         }

Damit ist die Web-Anwendung bereit zum Testen. So schaut zum Beispiel ein Test für das Laden der Seite aus:

    1 using framework.Testable.Web;

    2 using framework.Testable.Web.UI;

    3 using NUnit.Framework;

    4 using Rhino.Mocks;

    5 using TestableWebForm;

    6 

    7 namespace Tests

    8 {

    9     [TestFixture]

   10     public class WebFormTests

   11     {

   12         private IPage m_page;

   13         private IHttpRequest m_request;

   14         private IHttpResponse m_response;

   15         private DefaultPage m_defaultPage;

   16 

   17         [SetUp]

   18         public void Init()

   19         {

   20             m_page = MockRepository.GenerateStub<IPage>();

   21             m_request = MockRepository.GenerateStub<IHttpRequest>();

   22             m_response = MockRepository.GenerateStub<IHttpResponse>();

   23             m_page.Request = m_request;

   24             m_page.Response = m_response;

   25         }

   26 

   27         [Test]

   28         public void PageLoad_Loading_EmptyFields()

   29         {

   30             // Arrange

   31 

   32 

   33             // Act

   34             m_defaultPage = new DefaultPage();

   35             m_defaultPage.SetTestableObjects(m_page);

   36             m_defaultPage.Page_Load( null, null );

   37 

   38             // Assert

   39             Assert.IsEmpty( m_defaultPage.Value1.Text );

   40             Assert.IsEmpty( m_defaultPage.Value2.Text );

   41             Assert.IsEmpty(m_defaultPage.Result.Text);

   42         }

Und so für PostBack inklusive QueryString-Parameter:

   63         [Test]

   64         public void PageLoad_PostBackWithRequestValue_ResultIsCorrect()

   65         {

   66             // Arrange

   67             const int value1 = 1;

   68             const int value2 = 2;

   69             const int value3 = 3;

   70             m_request.Expect( a => a.GetParamValue( "multiple" ) ).IgnoreArguments().Repeat.Twice().Return( value3.ToString() );

   71             m_page.Expect( a => a.IsPostBack ).Return( true );

   72 

   73             // Act

   74             m_defaultPage = new DefaultPage();

   75             m_defaultPage.SetTestableObjects( m_page );

   76             m_defaultPage.Value1.Text = value1.ToString();

   77             m_defaultPage.Value2.Text = value2.ToString();

   78             m_defaultPage.Page_Load( null, null );

   79 

   80             // Assert

   81             Assert.IsTrue( m_defaultPage.Result.Text == ( (value1 + value2)*value3 ).ToString() );

   82         }

Dieses Vorgehen hat uns nicht nur den seit langen gesuchten Weg zum Testen von Webanwendungen geebnet, sondern zwingt auch den Entwickler dazu, alle Funktionalitäten, die nicht in einer Webseite sondern in die dll-s gehören, auszulagern. Damit dürfte es auch der richtige Weg der Clean Code Developers für die Arbeit mit WebForms sein.

Was die Adapter-Klassen betrifft: inzwischen haben wir auch System.IO “adaptiert” und bald werden die anderen System-Klassen folgen, je nach Bedarf.
Download VS2008-Projekt

kick it on dotnet-kicks.de

Share

Comments Keine Kommentare »

Wir fragen uns seit langem, wie man internal Methoden testen kann, da eins klar ist, die Kapselung darf nicht wegen der Testfähigkeit verletzt werden.
Letzte Woche hatte zum Glück auch jemand anderer dieses Problem und er hat gleich bei CCD-GoogleGroup nachgefragt. Und so haben wir auch erfahren, wie die Lösung lautet:

für “internal”-Elemente gibt es auch die Option mit [assembly:
InternalsVisibleTo("TestAssembly")] zu arbeiten. Alternativ kannst du die zu
testenden Klassen auch per “Add existing item” und dann “Add as link” (siehe
kleines Dreieck neben dem “Add”-Button) zum Testprojekt hinzufügen.

(Danke Alex)

Danach war nur noch ein wenig Surfen nötig, um alles zu erfahren:

msdn sagt:

InternalsVisibleToAttribute Class

Specifies that types that are ordinarily visible only within the current assembly are visible to another assembly.

User comment:
It is not documented anywhere to my knowledge, but if you want to grant “InternalsVisibleTo” permission to more than one assembly, you need to understand the syntax.

To do this you should NOT insert multiples instances of:

[assembly: InternalsVisibleTo("FirstAssembly")]

Instead do this:

[assembly: InternalsVisibleTo("FirstAssembly"),
InternalsVisibleTo("SecondAssembly"),
InternalsVisibleTo("ThirdAssembly")]

The former syntax is legal but fails, because each instance simply redefines and replaces any earlier ones, the latter syntax works as required.

kick it on dotnet-kicks.de

Share

Comments 1 Kommentar »