Answers: <p>Here's a <strong>very</strong> simple on how I implemented it. It works flawlessly for my needs. The lesson I learned is that the class name <strong>must</strong> match the file name, and as a corollary, there can only be <strong>one</strong> class definition per file.</p>
  <pre class="lang-java prettyprint-override"><code>File: Builder.groovy
  
  package com.company.builders
  abstract class Builder {
    def walk() {
      throw new Exception(&quot;Needs to be implemented in concrete classes&quot;)
    }
  
    protected Builder(final String color, final Integer numLegs) {
      this.color = color
      this.numLegs = numLegs
    }
  
    // Common to all widgets
    protected def changeColor() {
      // do something
    }
  
    // Attributes
    protected String color = null
    protected Integer numLegs = -1
    protected String shape = null
  }
  
  File: WidgetXBuilder.groovy
  
  package com.company.builders
  import com.company.builders.Builder
  class WidgetXBuilder extends Builder {
    WidgetXBuilder(final String color, final Integer numLegs) {
      super(color, numLegs)
    }
  
    def walk() {
      changeColor()
      // Implement Widget X walk function
    }
  
    protected String shape = &quot;round&quot;
  }
  
  
  File: WidgetYBuilder.groovy
  
  package com.company.builders
  import com.company.builders.Builder
  class WidgetYBuilder extends Builder {
    WidgetYBuilder(final String color, final Integer numLegs) {
      super(color, numLegs)
    }
  
    def walk() {
      changeColor()
      // Implement Widget Y walk function
    }
  
    protected String shape = &quot;square&quot;
  }
  
  
  Jenkinsfile
  
  @Library('shared-library') _
  import com.company.builders.WidgetXBuilder
  import com.company.builders.WidgetYBuilder
  
  node(&quot;linux&quot;) {
    def obj = new WidgetXBuilder(&quot;blue&quot;, 2)
    obj.walk() // walks like a blue widget X with 2 legs
    obj = new WidgetYBuilder(&quot;green&quot;, 4)
    obj.walk() // walks like a green widget Y with 4 legs
  }
  </code></pre>
  
Answers: CC BY-SA 4.0
Answers: 2024-01-24T03:04:57.523Z
Answers: 2024-01-24T03:04:57.523Z
Answers: Chris F
Answers: <p>Yes, The structure provided in the question is correct.</p>
  <p>With the setup provided above, it will achieve <strong>encapsulation</strong> through the use of an abstract base class (<code>AbstractBuilder</code>), <strong>inheritance</strong> by extending the base class in the widget builder classes (<code>WidgetXBuilder</code> and <code>WidgetYBuilder</code>), and <strong>polymorphism</strong> by overriding the <code>buildWidget</code> method in each builder class.</p>
  <p>The only change would be to use using <code>def widgetx = new WidgetXBuilder()</code> instead of <code>Builder widgetx = new WidgetXBuilder()</code>, it allows for  flexibility and dynamic typing in Groovy.</p>
  <p>This means that the type of the variable <code>widgetx</code> is determined by the type of the object created on the right side of the assignment (<code>new WidgetXBuilder()</code> in the code provided in your question).</p>
  <p>Here is an example pipeline (do not need to import <code>com.company.builders.WidgetXBuilder</code> etc. explicitly in your pipeline script if you are using the shared library):</p>
  <pre><code>@Library('my-shared-library') _
  
  def buildWidget(AbstractBuilder builder, String part0, String part1) {
    builder.buildWidget(part0, part1)
    return builder.getWidget()
  }
  
  pipeline {
    agent any
    
    stages {
      stage('Build Widgets') {
        steps {
          script {
            def widgetX = buildWidget(new com.company.builders.WidgetXBuilder(), &quot;part0&quot;, &quot;part1&quot;)
            echo &quot;WidgetX: ${widgetX}&quot;
            
            def widgetY = buildWidget(new com.company.builders.WidgetYBuilder(), &quot;part0&quot;, &quot;part1&quot;)
            echo &quot;WidgetY: ${widgetY}&quot;
          }
        }
      }
    }
  }
  
  </code></pre>
  
Answers: CC BY-SA 4.0
Answers: 2023-06-19T08:54:51.850Z
Answers: 2023-06-19T09:09:58.403Z
Answers: 2023-06-19T09:09:58.403Z
Answers: djmonki
Answers: <p>Inheritance is neither well-supported nor recommended for Jenkins pipeline. It may lead to runtime issues. Please refer these articles:<br></p>
  <ul>
  <li><a href="https://bmuschko.com/blog/jenkins-shared-libraries/" rel="nofollow noreferrer">https://bmuschko.com/blog/jenkins-shared-libraries/</a>.</li>
  <li><a href="https://docs.cloudbees.com/docs/cloudbees-ci-kb/latest/best-practices/inheritance-is-not-working-inside-of-my-pipelines-or-shared-libraries" rel="nofollow noreferrer">https://docs.cloudbees.com/docs/cloudbees-ci-kb/latest/best-practices/inheritance-is-not-working-inside-of-my-pipelines-or-shared-libraries</a></li>
  </ul>
  
Answers: CC BY-SA 4.0
Answers: 2024-01-19T08:00:38.083Z
Answers: 2024-01-19T08:00:38.083Z
Answers: Sid
Body: <p>I'm trying to implement the <code>Builder</code> design pattern in a Jenkins pipeline shared library. Say I have the following file structure and pseudo-code (to only illustrate the concept I'm after)</p>
  <pre><code>src/com/company/builders/
                          /Builder.groovy
                          /WidgetXBuilder.groovy
                          /WidgetYBuilder.groovy
  </code></pre>
  <p><code>Builder.groovy</code> has</p>
  <pre class="lang-java prettyprint-override"><code>package com.company.builders
  
  abstract class AbstractBuilder {
    protected String part0 = null
    protected String part1 = null
    protected String widget = null
  }
  
  // Implement in Widget builder classes
  def buildWidget(final String p0, final String p1) {
  }
  
  // Used by any Widget concrete class
  def getWidget() {
    return widget
  }
  </code></pre>
  <p><code>WidgetXBuilder.groovy</code> has</p>
  <pre class="lang-java prettyprint-override"><code>package com.company.builders
  
  class WidgetXBuilder extends AbstractBuilder {
  }
  
  // Overload buildWidget()
  def buildWidget(final String p0, final String p1) {
    widget = p0 + p1
  }
  </code></pre>
  <p>Similarly, <code>WidgetYBuilder.groovy</code> has</p>
  <pre class="lang-java prettyprint-override"><code>package com.company.builders
  
  class WidgetYBuilder extends AbstractBuilder {
  }
  
  // Overload buildWidget()
  def buildWidget(final String p0, final String p1) {
    widget = p0 - p1
  }
  
  </code></pre>
  <p>Now I simply want a client to do this. Note <code>my-shared-library</code> is a shared library that I set up in my Jenkins system configuration that has the Git repo of above file structure.</p>
  <pre class="lang-java prettyprint-override"><code>@Library('my-shared-library') _
  import com.company.builders.Builder
  import com.company.builders.WidgetXBuilder
  import com.company.builders.WidgetYBuilder
  
  // Build WidgetX
  Builder widgetx = new WidgetXBuilder()
  widgetx.buildWidget(&quot;part0&quot;, &quot;part1&quot;) // Build a WidgetX widget
  widgetx.getWidget() // get the WidgetX widget
  
  // Build WidgetY
  Builder widgety = new WidgetYBuilder()
  widgety.buildWidget(&quot;part0&quot;, &quot;part1&quot;) // Build a WidgetY widget
  widgety.getWidget() // get the WidgetY widget
  </code></pre>
  <p>Can I achieve this with shared libraries? That is, implement the concept of encapsulation, inheritance, and polymorphism using a shared library? How? TIA</p>
  
Comments: CC BY-SA 4.0
Comments: 2023-07-01T14:36:59.393Z
Comments: I have some examples in my shared library https://github.com/DontShaveTheYak/jenkins-std-lib but they probably are not that great. But the "ToolInstaller" is pretty neat https://github.com/DontShaveTheYak/jenkins-std-lib/tree/master/src/org/dsty/system/os/programs
Comments: Levi
Tags: <jenkins><jenkins-pipeline><groovy>
Title: How can I use a pipeline Jenkins shared library to implement encapsulation, inheritance, and polymorphism?
UserDisplayName: Chris F
ThreadText: # How can I use a pipeline Jenkins shared library to implement encapsulation, inheritance, and polymorphism?
  
  (Asked by: username_0 on 2023-06-17)
  
  I'm trying to implement the `Builder` design pattern in a Jenkins pipeline shared library. Say I have the following file structure and pseudo-code (to only illustrate the concept I'm after)
  
  ```
  src/com/company/builders/
                          /Builder.groovy
                          /WidgetXBuilder.groovy
                          /WidgetYBuilder.groovy
  
  ```
  
  `Builder.groovy` has
  
  ```java
  package com.company.builders
  
  abstract class AbstractBuilder {
    protected String part0 = null
    protected String part1 = null
    protected String widget = null
  }
  
  // Implement in Widget builder classes
  def buildWidget(final String p0, final String p1) {
  }
  
  // Used by any Widget concrete class
  def getWidget() {
    return widget
  }
  
  ```
  
  `WidgetXBuilder.groovy` has
  
  ```java
  package com.company.builders
  
  class WidgetXBuilder extends AbstractBuilder {
  }
  
  // Overload buildWidget()
  def buildWidget(final String p0, final String p1) {
    widget = p0 + p1
  }
  
  ```
  
  Similarly, `WidgetYBuilder.groovy` has
  
  ```java
  package com.company.builders
  
  class WidgetYBuilder extends AbstractBuilder {
  }
  
  // Overload buildWidget()
  def buildWidget(final String p0, final String p1) {
    widget = p0 - p1
  }
  
  ```
  
  Now I simply want a client to do this. Note `my-shared-library` is a shared library that I set up in my Jenkins system configuration that has the Git repo of above file structure.
  
  ```java
  @Library('my-shared-library') _
  import com.company.builders.Builder
  import com.company.builders.WidgetXBuilder
  import com.company.builders.WidgetYBuilder
  
  // Build WidgetX
  Builder widgetx = new WidgetXBuilder()
  widgetx.buildWidget("part0", "part1") // Build a WidgetX widget
  widgetx.getWidget() // get the WidgetX widget
  
  // Build WidgetY
  Builder widgety = new WidgetYBuilder()
  widgety.buildWidget("part0", "part1") // Build a WidgetY widget
  widgety.getWidget() // get the WidgetY widget
  
  ```
  
  Can I achieve this with shared libraries? That is, implement the concept of encapsulation, inheritance, and polymorphism using a shared library? How? TIA
  
  **Tags:** jenkins, jenkins-pipeline, groovy
  
  > **Comments:**
  > **username_1**: I have some examples in my shared library https://github.com/DontShaveTheYak/jenkins-std-lib but they probably are not that great. But the "ToolInstaller" is pretty neat https://github.com/DontShaveTheYak/jenkins-std-lib/tree/master/src/org/dsty/system/os/programs  
  
  ---
  
  ## 3 answers
  
  ### Answer by username_0 on 2024-01-24. Score: 0 | Accepted answer
  
  Here's a **very** simple on how I implemented it. It works flawlessly for my needs. The lesson I learned is that the class name **must** match the file name, and as a corollary, there can only be **one** class definition per file.
  
  ```java
  File: Builder.groovy
  
  package com.company.builders
  abstract class Builder {
    def walk() {
      throw new Exception("Needs to be implemented in concrete classes")
    }
  
    protected Builder(final String color, final Integer numLegs) {
      this.color = color
      this.numLegs = numLegs
    }
  
    // Common to all widgets
    protected def changeColor() {
      // do something
    }
  
    // Attributes
    protected String color = null
    protected Integer numLegs = -1
    protected String shape = null
  }
  
  File: WidgetXBuilder.groovy
  
  package com.company.builders
  import com.company.builders.Builder
  class WidgetXBuilder extends Builder {
    WidgetXBuilder(final String color, final Integer numLegs) {
      super(color, numLegs)
    }
  
    def walk() {
      changeColor()
      // Implement Widget X walk function
    }
  
    protected String shape = "round"
  }
  
  File: WidgetYBuilder.groovy
  
  package com.company.builders
  import com.company.builders.Builder
  class WidgetYBuilder extends Builder {
    WidgetYBuilder(final String color, final Integer numLegs) {
      super(color, numLegs)
    }
  
    def walk() {
      changeColor()
      // Implement Widget Y walk function
    }
  
    protected String shape = "square"
  }
  
  Jenkinsfile
  
  @Library('shared-library') _
  import com.company.builders.WidgetXBuilder
  import com.company.builders.WidgetYBuilder
  
  node("linux") {
    def obj = new WidgetXBuilder("blue", 2)
    obj.walk() // walks like a blue widget X with 2 legs
    obj = new WidgetYBuilder("green", 4)
    obj.walk() // walks like a green widget Y with 4 legs
  }
  
  ```
  
  
  ---
  
  ### Answer by username_2 on 2023-06-19. Score: 1
  
  Yes, The structure provided in the question is correct.
  
  With the setup provided above, it will achieve **encapsulation** through the use of an abstract base class (`AbstractBuilder`), **inheritance** by extending the base class in the widget builder classes (`WidgetXBuilder` and `WidgetYBuilder`), and **polymorphism** by overriding the `buildWidget` method in each builder class.
  
  The only change would be to use using `def widgetx = new WidgetXBuilder()` instead of `Builder widgetx = new WidgetXBuilder()`, it allows for flexibility and dynamic typing in Groovy.
  
  This means that the type of the variable `widgetx` is determined by the type of the object created on the right side of the assignment (`new WidgetXBuilder()` in the code provided in your question).
  
  Here is an example pipeline (do not need to import `com.company.builders.WidgetXBuilder` etc. explicitly in your pipeline script if you are using the shared library):
  
  ```
  @Library('my-shared-library') _
  
  def buildWidget(AbstractBuilder builder, String part0, String part1) {
    builder.buildWidget(part0, part1)
    return builder.getWidget()
  }
  
  pipeline {
    agent any
    
    stages {
      stage('Build Widgets') {
        steps {
          script {
            def widgetX = buildWidget(new com.company.builders.WidgetXBuilder(), "part0", "part1")
            echo "WidgetX: ${widgetX}"
            
            def widgetY = buildWidget(new com.company.builders.WidgetYBuilder(), "part0", "part1")
            echo "WidgetY: ${widgetY}"
          }
        }
      }
    }
  }
  
  ```
  
  
  ---
  
  ### Answer by username_3 on 2024-01-19. Score: -1
  
  Inheritance is neither well-supported nor recommended for Jenkins pipeline. It may lead to runtime issues. Please refer these articles:
  
  * <https://bmuschko.com/blog/jenkins-shared-libraries/>.
  * <https://docs.cloudbees.com/docs/cloudbees-ci-kb/latest/best-practices/inheritance-is-not-working-inside-of-my-pipelines-or-shared-libraries>
  
  
  ---
  
  