AEM開発者ブログ by YAMATO

アドビ社のデリバリーパートナー大和株式会社のAEM開発者ブログです。

Sling Models vs WCMUsePojo

AEM Developerの皆様お疲れさまです。大和株式会社の狩野です。

2021年初の更新となる今回はバックエンド側の実装方法であるWCMUsePojoとSling Modelsの2つの比較について行いたいと思います。

バックエンド側の実装方法にWCJUsePojoとSling Modelsの2つが存在することに疑問を持っていた人、そもそもバックエンド側の実装方法について知りたい人にはぜひ読んでほしい記事です。

結論

これから新しく書くコードは可能な限りSling Modelsにするのが望ましいです。
Sling ModelsはHTL以外でも再利用が可能だったり、単体テストが容易だったり、他クラスを継承することが可能だったりと利点が多いからです。
WCMUsePojoはHTL以外で再利用が不可であったり、WCMUsePojoを継承するという形で実装するため、他クラスを継承できない上にWSMUsePojoクラス持つメソッドは全てがfinalで宣言されているため、モック化が難しいという問題があります。

WCMUsePojoとSling Modelsの比較

文法

Sling Models

アノテーションでメソッドやフィールドに意味をもたせます。

@Model(
    adaptables = { SlingHttpServletRequest.class },
    defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class SlingModelTutorial {
    @ValueMapValue
    private String dialogValue;

    private String toHtl;

    @PostConstruct
    public void init() {
        toHtl = dialogValue.toUpperCase();
    }

    public String getToHtl() {
        return toHtl;
    }
}

WCMUsePojo

クラス名にもある通り、純粋なJavaで書かれたObjectで、WCMUsePojoクラスが持っているメソッドを使って必要なオブジェクトを取得します。

public class PojoTutorial extends WCMUsePojo {
    private String toHtl;

    @Override
    public void activate() {
        ValueMap properties = getProperties();
        toHtl = properties.get("dialogValue", "");
    }

    public String getToHtl() {
        return toHtl;
    }
}

機能面での比較

  • HTL以外での再利用
    • 実際これができる利点というのを今のところ私は感じたことがないのですが、多くのサイトで語られていたので利点としてあげておきます
  • 単体テストが容易
    • Sling Modelsは AemContext というクラスを利用して簡単に単体テストが可能
    • WCMUsePojoはこのクラスが持っているメソッドが final で宣言されているため、モック化することが難しく単体テストが少し難しくなる(できないわけではない)
  • 他クラスの継承
    • Sling Modelsはアノテーション駆動なので、これを使うために他クラスを継承する必要が無く、継承が可能
    • WCMUsePojoはこのクラスを継承した上で動くため、多重継承が禁止されているJavaにおいて継承ができない

単体テストの比較

Sling Models

@ExtendWith(AemContextExtension.class)
class SlingModelTutorialTest {
    @Rule
    private final AemContext ctx = new AemContext();

    @BeforeEach
    void setUp() {
        ctx.addModelsForClasses(SlingModelTutorial.class);
        ctx.load().json("/path/to/resource/json/path.json", "/content/myapp/");
    }

    @Test
    void getToHtlTest() {
        String expected = "dialog_value";
        ctx.currentResource("/content/myapp/path/to/node");
        SlingModelTutorial slingModelTutorial = ctx.request().adaptTo(SlingModelTutorial.class);
        String actual = slingModelTutorial.getToHtl();
        assertEquals(expected, actual);
    }
}

AemContext というクラスを利用して、ノード構造を記したJsonをテスト用データに使用可能

WCMUsePojo

public class PojoTutorialTest {
    private ValueMap properties = new ValueMapDecorator(new HashMap<String, Object>());
    private PojoTutorial pojoTutorial = PowerMock.spy(new PojoTutorial());

    @BeforeEach
    public void setUp() {
        properties.put("dialogValue", "dialog_value");
        PowerMockito.when(pojoTutorial, "getProperties").thenReturn(properties);
    }

    public void getToHtlTest() {
        String expected = "dialog_value";
        pojoTutorial.activate();
        String actual = pojoTutorial.getToHtl();
        assertEquals(expected, actual);
    }
}

テストコード上でproperties.put()を実行することによってpropertiesを作成しなければならない上に、 getProperties 等のメソッドがfinalで宣言されているため、PowerMockitoでmock化する必要がある。

最後に

以上となります。
技術の選択で迷ったら新しい技術を使いましょうということですね。