Singleton service class with inheritance












0












$begingroup$


There is a service class (ProposalPdfService) in one of our assemblies that has implementation logic specific to that app's assembly. Previously (and, I believe, erroneously) I treated it as a quasi-singleton by making it and all its members static, but I refactored it to use the singleton pattern described here using the Lazy<T> type.



Upon needing to share a generic subset of this service's functionality with another assembly, I decided to move that functionality into a base class (PdfServiceBase) in a shared assembly used by both apps.



This second app can use the base service as-is — there are currently no plans to add any implementation-specific logic for this app — while the main app uses ProposalPdfService, which inherits from PdfServiceBase. Both classes are singletons.



public class PdfServiceBase
{
private static readonly Lazy<PdfServiceBase> lazy = new Lazy<PdfServiceBase>(() => new PdfServiceBase());

public static PdfServiceBase Instance { get => lazy.Value; }

protected PdfServiceBase() { }

public PdfDocument RetrievePdfFile(string relativeFilePath, string relativeFileName)
{
PdfDocument pdf = null;
string directoryWithoutLastBackSlash = FilePathManager.GetDocumentsFilePath("PDFs");
string filePath = directoryWithoutLastBackSlash + @"" + relativeFilePath;
string fileName = filePath + @"" + relativeFileName + ".pdf";

// Check if file exists
if (System.IO.File.Exists(fileName))
pdf = PdfDocument.FromFile(fileName);

return pdf;
}
}




public sealed class ProposalPdfService : PdfServiceBase
{
private static readonly Lazy<ProposalPdfService> lazy = new Lazy<ProposalPdfService>(() => new ProposalPdfService());

public static new ProposalPdfService Instance { get => lazy.Value; }

private ProposalPdfService() { }

// app-specific service members listed below...
public string Foo { get; } = "foo";
}


I specifically chose inheritance over containment because, in my opinion, it would be very clumsy to have to remember to use the base class service just to use the RetrievePdfFile method. Containment would also require having an instance of ProposalPdfService and PdfServiceBase. Being able to invoke ProposalPdfService.RetrievePdfFile makes good sense to me.



The only thing that feels "weird" about this implementation to me is that, due to both classes being able to be instantiated as independent singletons, they both have Instance members, meaning that the derived property has to shadow the base property using the new keyword. And I know shadowing is one of those things people typically associate with poor design.



I'm generally happy with this implementation, but as this is my first time implementing a "real" singleton and, due to the inheritance, it's not so straight forward, I wanted to get some feedback on it. Do you see any problems with this approach? Or, if it's the right approach, are there any improvements I could make?










share|improve this question









New contributor




Jacob Stamm is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$

















    0












    $begingroup$


    There is a service class (ProposalPdfService) in one of our assemblies that has implementation logic specific to that app's assembly. Previously (and, I believe, erroneously) I treated it as a quasi-singleton by making it and all its members static, but I refactored it to use the singleton pattern described here using the Lazy<T> type.



    Upon needing to share a generic subset of this service's functionality with another assembly, I decided to move that functionality into a base class (PdfServiceBase) in a shared assembly used by both apps.



    This second app can use the base service as-is — there are currently no plans to add any implementation-specific logic for this app — while the main app uses ProposalPdfService, which inherits from PdfServiceBase. Both classes are singletons.



    public class PdfServiceBase
    {
    private static readonly Lazy<PdfServiceBase> lazy = new Lazy<PdfServiceBase>(() => new PdfServiceBase());

    public static PdfServiceBase Instance { get => lazy.Value; }

    protected PdfServiceBase() { }

    public PdfDocument RetrievePdfFile(string relativeFilePath, string relativeFileName)
    {
    PdfDocument pdf = null;
    string directoryWithoutLastBackSlash = FilePathManager.GetDocumentsFilePath("PDFs");
    string filePath = directoryWithoutLastBackSlash + @"" + relativeFilePath;
    string fileName = filePath + @"" + relativeFileName + ".pdf";

    // Check if file exists
    if (System.IO.File.Exists(fileName))
    pdf = PdfDocument.FromFile(fileName);

    return pdf;
    }
    }




    public sealed class ProposalPdfService : PdfServiceBase
    {
    private static readonly Lazy<ProposalPdfService> lazy = new Lazy<ProposalPdfService>(() => new ProposalPdfService());

    public static new ProposalPdfService Instance { get => lazy.Value; }

    private ProposalPdfService() { }

    // app-specific service members listed below...
    public string Foo { get; } = "foo";
    }


    I specifically chose inheritance over containment because, in my opinion, it would be very clumsy to have to remember to use the base class service just to use the RetrievePdfFile method. Containment would also require having an instance of ProposalPdfService and PdfServiceBase. Being able to invoke ProposalPdfService.RetrievePdfFile makes good sense to me.



    The only thing that feels "weird" about this implementation to me is that, due to both classes being able to be instantiated as independent singletons, they both have Instance members, meaning that the derived property has to shadow the base property using the new keyword. And I know shadowing is one of those things people typically associate with poor design.



    I'm generally happy with this implementation, but as this is my first time implementing a "real" singleton and, due to the inheritance, it's not so straight forward, I wanted to get some feedback on it. Do you see any problems with this approach? Or, if it's the right approach, are there any improvements I could make?










    share|improve this question









    New contributor




    Jacob Stamm is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.







    $endgroup$















      0












      0








      0





      $begingroup$


      There is a service class (ProposalPdfService) in one of our assemblies that has implementation logic specific to that app's assembly. Previously (and, I believe, erroneously) I treated it as a quasi-singleton by making it and all its members static, but I refactored it to use the singleton pattern described here using the Lazy<T> type.



      Upon needing to share a generic subset of this service's functionality with another assembly, I decided to move that functionality into a base class (PdfServiceBase) in a shared assembly used by both apps.



      This second app can use the base service as-is — there are currently no plans to add any implementation-specific logic for this app — while the main app uses ProposalPdfService, which inherits from PdfServiceBase. Both classes are singletons.



      public class PdfServiceBase
      {
      private static readonly Lazy<PdfServiceBase> lazy = new Lazy<PdfServiceBase>(() => new PdfServiceBase());

      public static PdfServiceBase Instance { get => lazy.Value; }

      protected PdfServiceBase() { }

      public PdfDocument RetrievePdfFile(string relativeFilePath, string relativeFileName)
      {
      PdfDocument pdf = null;
      string directoryWithoutLastBackSlash = FilePathManager.GetDocumentsFilePath("PDFs");
      string filePath = directoryWithoutLastBackSlash + @"" + relativeFilePath;
      string fileName = filePath + @"" + relativeFileName + ".pdf";

      // Check if file exists
      if (System.IO.File.Exists(fileName))
      pdf = PdfDocument.FromFile(fileName);

      return pdf;
      }
      }




      public sealed class ProposalPdfService : PdfServiceBase
      {
      private static readonly Lazy<ProposalPdfService> lazy = new Lazy<ProposalPdfService>(() => new ProposalPdfService());

      public static new ProposalPdfService Instance { get => lazy.Value; }

      private ProposalPdfService() { }

      // app-specific service members listed below...
      public string Foo { get; } = "foo";
      }


      I specifically chose inheritance over containment because, in my opinion, it would be very clumsy to have to remember to use the base class service just to use the RetrievePdfFile method. Containment would also require having an instance of ProposalPdfService and PdfServiceBase. Being able to invoke ProposalPdfService.RetrievePdfFile makes good sense to me.



      The only thing that feels "weird" about this implementation to me is that, due to both classes being able to be instantiated as independent singletons, they both have Instance members, meaning that the derived property has to shadow the base property using the new keyword. And I know shadowing is one of those things people typically associate with poor design.



      I'm generally happy with this implementation, but as this is my first time implementing a "real" singleton and, due to the inheritance, it's not so straight forward, I wanted to get some feedback on it. Do you see any problems with this approach? Or, if it's the right approach, are there any improvements I could make?










      share|improve this question









      New contributor




      Jacob Stamm is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.







      $endgroup$




      There is a service class (ProposalPdfService) in one of our assemblies that has implementation logic specific to that app's assembly. Previously (and, I believe, erroneously) I treated it as a quasi-singleton by making it and all its members static, but I refactored it to use the singleton pattern described here using the Lazy<T> type.



      Upon needing to share a generic subset of this service's functionality with another assembly, I decided to move that functionality into a base class (PdfServiceBase) in a shared assembly used by both apps.



      This second app can use the base service as-is — there are currently no plans to add any implementation-specific logic for this app — while the main app uses ProposalPdfService, which inherits from PdfServiceBase. Both classes are singletons.



      public class PdfServiceBase
      {
      private static readonly Lazy<PdfServiceBase> lazy = new Lazy<PdfServiceBase>(() => new PdfServiceBase());

      public static PdfServiceBase Instance { get => lazy.Value; }

      protected PdfServiceBase() { }

      public PdfDocument RetrievePdfFile(string relativeFilePath, string relativeFileName)
      {
      PdfDocument pdf = null;
      string directoryWithoutLastBackSlash = FilePathManager.GetDocumentsFilePath("PDFs");
      string filePath = directoryWithoutLastBackSlash + @"" + relativeFilePath;
      string fileName = filePath + @"" + relativeFileName + ".pdf";

      // Check if file exists
      if (System.IO.File.Exists(fileName))
      pdf = PdfDocument.FromFile(fileName);

      return pdf;
      }
      }




      public sealed class ProposalPdfService : PdfServiceBase
      {
      private static readonly Lazy<ProposalPdfService> lazy = new Lazy<ProposalPdfService>(() => new ProposalPdfService());

      public static new ProposalPdfService Instance { get => lazy.Value; }

      private ProposalPdfService() { }

      // app-specific service members listed below...
      public string Foo { get; } = "foo";
      }


      I specifically chose inheritance over containment because, in my opinion, it would be very clumsy to have to remember to use the base class service just to use the RetrievePdfFile method. Containment would also require having an instance of ProposalPdfService and PdfServiceBase. Being able to invoke ProposalPdfService.RetrievePdfFile makes good sense to me.



      The only thing that feels "weird" about this implementation to me is that, due to both classes being able to be instantiated as independent singletons, they both have Instance members, meaning that the derived property has to shadow the base property using the new keyword. And I know shadowing is one of those things people typically associate with poor design.



      I'm generally happy with this implementation, but as this is my first time implementing a "real" singleton and, due to the inheritance, it's not so straight forward, I wanted to get some feedback on it. Do you see any problems with this approach? Or, if it's the right approach, are there any improvements I could make?







      c# inheritance singleton






      share|improve this question









      New contributor




      Jacob Stamm is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question









      New contributor




      Jacob Stamm is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question








      edited 4 hours ago







      Jacob Stamm













      New contributor




      Jacob Stamm is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked 4 hours ago









      Jacob StammJacob Stamm

      1012




      1012




      New contributor




      Jacob Stamm is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      Jacob Stamm is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      Jacob Stamm is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






















          1 Answer
          1






          active

          oldest

          votes


















          0












          $begingroup$

          I believe a requirement of a “true” singleton is that it’s supposed to be sealed and can’t be inherited. The very article you linked states as much. This is why things feel a bit off to you, you’re only respecting the pattern for one of the classes.



          Consider having PdfServiceBase be an abstract class that implements RetrievePdfFile() and any other common code like that, then implement ProposalPdfService and something like just PdfService as singletons each implementing the pattern properly and separately, without “new”.






          share|improve this answer








          New contributor




          AKD is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.






          $endgroup$













            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });






            Jacob Stamm is a new contributor. Be nice, and check out our Code of Conduct.










            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214280%2fsingleton-service-class-with-inheritance%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0












            $begingroup$

            I believe a requirement of a “true” singleton is that it’s supposed to be sealed and can’t be inherited. The very article you linked states as much. This is why things feel a bit off to you, you’re only respecting the pattern for one of the classes.



            Consider having PdfServiceBase be an abstract class that implements RetrievePdfFile() and any other common code like that, then implement ProposalPdfService and something like just PdfService as singletons each implementing the pattern properly and separately, without “new”.






            share|improve this answer








            New contributor




            AKD is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            $endgroup$


















              0












              $begingroup$

              I believe a requirement of a “true” singleton is that it’s supposed to be sealed and can’t be inherited. The very article you linked states as much. This is why things feel a bit off to you, you’re only respecting the pattern for one of the classes.



              Consider having PdfServiceBase be an abstract class that implements RetrievePdfFile() and any other common code like that, then implement ProposalPdfService and something like just PdfService as singletons each implementing the pattern properly and separately, without “new”.






              share|improve this answer








              New contributor




              AKD is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
              Check out our Code of Conduct.






              $endgroup$
















                0












                0








                0





                $begingroup$

                I believe a requirement of a “true” singleton is that it’s supposed to be sealed and can’t be inherited. The very article you linked states as much. This is why things feel a bit off to you, you’re only respecting the pattern for one of the classes.



                Consider having PdfServiceBase be an abstract class that implements RetrievePdfFile() and any other common code like that, then implement ProposalPdfService and something like just PdfService as singletons each implementing the pattern properly and separately, without “new”.






                share|improve this answer








                New contributor




                AKD is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.






                $endgroup$



                I believe a requirement of a “true” singleton is that it’s supposed to be sealed and can’t be inherited. The very article you linked states as much. This is why things feel a bit off to you, you’re only respecting the pattern for one of the classes.



                Consider having PdfServiceBase be an abstract class that implements RetrievePdfFile() and any other common code like that, then implement ProposalPdfService and something like just PdfService as singletons each implementing the pattern properly and separately, without “new”.







                share|improve this answer








                New contributor




                AKD is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.









                share|improve this answer



                share|improve this answer






                New contributor




                AKD is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.









                answered 1 hour ago









                AKDAKD

                212




                212




                New contributor




                AKD is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.





                New contributor





                AKD is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.






                AKD is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.






















                    Jacob Stamm is a new contributor. Be nice, and check out our Code of Conduct.










                    draft saved

                    draft discarded


















                    Jacob Stamm is a new contributor. Be nice, and check out our Code of Conduct.













                    Jacob Stamm is a new contributor. Be nice, and check out our Code of Conduct.












                    Jacob Stamm is a new contributor. Be nice, and check out our Code of Conduct.
















                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214280%2fsingleton-service-class-with-inheritance%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    How to reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

                    is 'sed' thread safe

                    How to make a Squid Proxy server?