Decorator design pattern in SystemVerilog

How I can design decorator pattern in system verilog.
I tried to write decorator pattern based on Java code on website, but it does not work.
It seems object chaining is terminated at the one reference and no 2nd or more reference is observed. I ran below in VCS and IES.

The situation of the following sample code is to calculate total cost(Coffee::cost()) when add some toppings (Milk or Mocha) on coffee.



class Coffee;
   function new();
   endfunction

   function int cost();
      return 1000;
   endfunction
endclass

virtual class DecoratorBase extends Coffee;
   pure virtual function int cost();
   pure virtual function Coffee decorate(Coffee coffee);
endclass

class Mocha extends DecoratorBase;
   Coffee coffee; 
   function new();
   endfunction 

   function Coffee decorate(Coffee coffee);
      this.coffee=coffee;
      return this.coffee;
   endfunction

   function int cost();
      return coffee.cost()+10;
   endfunction
endclass

class Milk extends DecoratorBase;
   Coffee coffee;
   function new();
   endfunction

   function Coffee decorate(Coffee coffee);
      this.coffee=coffee;
      return this.coffee;
   endfunction
   
   function int cost();
      return coffee.cost()+100;
   endfunction
endclass
  
module tb();
   initial begin
      Coffee c1;
      Mocha  d1, d2;
      Milk   d3;
      Coffee x1; // pointer to Coffee object

      c1 = new();
      d1 = new(); d2 = new(); d3 = new();
      x1=d1.decorate(c1);
      x1=d2.decorate(x1);
      x1=d3.decorate(x1);
//    $display("## x1 cost is %d", d3.cost()); //  1100 (1000+ 100 is executed) 
      $display("## x1 cost is %d", x1.cost()); //  1000 (I expect to yield 1120; 1000+10+10+100)
   end
endmodule // tb

In Java code:


class Coffee
{
    public cost(){return 1.5;}
}

// Abstract Decorator
class CondimentDecorator extends Coffee
{
}

// Concrete Decorator
class Mocha extends ComdimentDecorator
{
    Coffee coffee;
    public Mocha(Coffee coffee){this.coffee = coffee;}
    public cost(){return .2 + coffee.cost();}
}

class Milk extends ComdimentDecorator
{
    Coffee coffee;
    public Milk(Coffee coffee){this.coffee = coffee;}
    public cost(){return .5 + coffee.cost();}
}
----- 
Coffee c1 = new Coffee();
c1 = new Mocha(c1);
c1 = new Mocha(c1);
c1 = new Milk(c1);
c1.cost(); // 

Thank you for your attention.
Regards

In reply to tsb_matumoto:

Two issues. First, “cost” needs to be virtual in Coffee. Otherwise in things like “Milk” if the coffee object is already a Mocha, you’ll end up just calling Coffee::cost rather than the overridden cost from Mocha. Second issue is that your “decorate” routines are incorrect – they are returning “this.coffee” and should be returning just “this”. Otherwise “x1=d2.decorate(x1);” ends up with the original “x1” not the “decorated x1”.

Based on your example (from Wikipedia perhaps?), you could also use “interface class” construction to more closely resemble what you’d do in Java. Below is a full working example (works in Questa at least) based on your code and using interface class constructs.


interface class Coffee;
   pure virtual function int cost();
endclass

class SimpleCoffee implements Coffee;
   virtual function int cost();
       return 1000;
   endfunction
endclass
 
virtual class DecoratorBase implements Coffee;
   pure virtual function int cost();
   pure virtual function Coffee decorate(Coffee coffee);
endclass
 
class Mocha extends DecoratorBase;
   Coffee coffee; 
   function Coffee decorate(Coffee coffee);
      this.coffee=coffee;
      return this;
   endfunction
 
   function int cost();
      return coffee.cost()+10;
   endfunction
endclass
 
class Milk extends DecoratorBase;
   Coffee coffee;
   function Coffee decorate(Coffee coffee);
      this.coffee=coffee;
      return this;
   endfunction
 
   function int cost();
      return coffee.cost()+100;
   endfunction
endclass
 
module tb();
   initial begin
      Coffee c1;
      SimpleCoffee sc;
      Mocha  d1, d2;
      Milk   d3;
 
      sc = new;
      c1 = sc;
      d1 = new(); d2 = new(); d3 = new();
      c1=d1.decorate(c1);
      c1=d2.decorate(c1);
      c1=d3.decorate(c1);

      $display("## x1 cost is %d", c1.cost()); //  1000 (I expect to yield 1120; 1000+10+10+100)
   end
endmodule // tb