<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>

CC BY-SA 4.0
2024-01-24T03:04:57.523Z
2024-01-24T03:04:57.523Z
Chris F
<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>

CC BY-SA 4.0
2023-06-19T08:54:51.850Z
2023-06-19T09:09:58.403Z
2023-06-19T09:09:58.403Z
djmonki
<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>

CC BY-SA 4.0
2024-01-19T08:00:38.083Z
2024-01-19T08:00:38.083Z
Sid
<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>

CC BY-SA 4.0
2023-07-01T14:36:59.393Z
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
Levi
<jenkins><jenkins-pipeline><groovy>
How can I use a pipeline Jenkins shared library to implement encapsulation, inheritance, and polymorphism?
Chris F
# 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
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
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
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.
@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.
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>


---

