2

I am currently adding additions to someone else's code and have come across a PdfBuilder class which is using the PdfSharp dll. Up until this point the class has taken a type called BoxRequest in the constructor. like so:

    public PdfDocument GenerateAddressLabelPdf(int id)
    {
        var box = _unitOfWork.BoxRepository.GetById(id);
        var generator = new PdfBuilder(box);
        return generator.BuildPdfDocument();
    }

And the PdfBuilder class looks like this:

    public class PdfBuilder
    {
        private readonly BoxRequest _boxRequest;

        public PdfBuilder(BoxRequest boxRequest)
        {
            _boxRequest = boxRequest;
        }

        public PdfDocument BuildPdfDocument()
        {
            PdfDocument pdfDocument = new PdfDocument();

            PdfPage page = pdfDocument.AddPage();
            page.Orientation = PageOrientation.Landscape;

            XGraphics gfx = XGraphics.FromPdfPage(page);
            XFont font = new XFont("Arial", 40, XFontStyle.Regular);
            XTextFormatter tf = new XTextFormatter(gfx);
            XRect rect = new XRect(0, (page.Height/4), page.Width, page.Height);
            gfx.DrawRectangle(XBrushes.White, rect);
            tf.Alignment = XParagraphAlignment.Center;
            tf.DrawString(GetText(), font, XBrushes.Black, rect);          

            return pdfDocument;
        }

        private string GetText()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(_boxRequest.Name);
            sb.AppendLine(_boxRequest.Department);
            sb.AppendLine(_boxRequest.Location.LocationName);
            return sb.ToString();
        }
    }

So as you can see the properties used in GetText are taken from the FileRequest type.

I now need to use this class again, this time for a new type called FileRequest, As soon as I made the call to this class and saw it only takes a BoxRequest I thought this could be a good thing to make generic.

So far I have changed the class like so:

    public class PdfBuilder<T>
    {
        private readonly T _pdfType;

        public PdfBuilder(T t)
        {
            _pdfType = t;
        }

        //additional class stuff
     }

But now I am a little confused as to how I should make the GetText method generic, as I need to render certain lines for each and every type.

At this point I think I could pass the type to GetText and then check the type and append different lines based on type, but it seems like its no longer really generic?

5
  • 1
    It's unlikely you need generics. You either need to extract interface from BoxRequest and FileRequest or\and create separate PdfBuilder classes for them (inheriting from one of course). Commented Nov 14, 2016 at 12:00
  • 1
    Perhaps you can make T an interface implemented by all your concrete objects? Commented Nov 14, 2016 at 12:01
  • 1
    if all thats different is GetText how about overriding the BoxRequest and the other classes ToStringmethod and not caring about the type at all? You could the replace GetText() with sth. input.ToString() otherwise, extract GetText() to an interface and let both of cour classes implement that in a proper way Commented Nov 14, 2016 at 12:09
  • Does FileRequest object have Name, Departament and Location.LocationName properties, just as BoxRequest? That is: is the way to print exactly the same or is it different? Commented Nov 14, 2016 at 12:11
  • Yet another way is to pass function which builds text to constructor: public PdfBuilder(Func<string> textBuilder). After all your PdfBuilder does not care about specific requets (at least in code shown). All what it cares about is to provide some text. Commented Nov 14, 2016 at 12:19

1 Answer 1

3

I don't think generics are the best way of handling this issue...

If all thats different is GetText the simplest (not best) way to resolve this would be overriding the BoxRequest and the other classes ToString() method and not caring about the type at all.

public class BoxRequest
{
    // ...

    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(this.Name);
        sb.AppendLine(this.Department);
        sb.AppendLine(this.Location.LocationName);
        return sb.ToString();
    }
}

public class PdfBuilder
{
    private readonly object request;

    public PdfBuilder(object request)
    {
        this.request = request;
    }

    public PdfDocument BuildPdfDocument()
    {
        // ...

        tf.DrawString(request.ToString(), font, XBrushes.Black, rect);          

        // ...
    }

}

This way, you could use the class to PDF any object. If this is to generic for you (for me it is), you could extract an interface for ToString()/GetText() and let your PdfBuilder consume that (which would be way cleaner IMO).

public interface IPdfConvertible
{
    public string GetText()
}

// same for FileRequest...
public class BoxRequest : IPdfConvertible
{
    // ...

    public string GetText()
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(this.Name);
        sb.AppendLine(this.Department);
        sb.AppendLine(this.Location.LocationName);
        return sb.ToString();
    }
}

public class PdfBuilder
{
    private readonly IPdfConvertible request;

    public PdfBuilder(IPdfConvertible request)
    {
        this.request = request;
    }

    public PdfDocument BuildPdfDocument()
    {
        // ...

        tf.DrawString(request.GetText(), font, XBrushes.Black, rect);          

        // ...
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

I am personally in favor of declaring a new IPdfPrintable interface with GetText() method so that: 1) PdfBuilder requires an object of that interface (and not any object); 2) the well known meaning of ToString() is unchanged.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.