質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.48%

  • JavaScript

    17083questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Node.js

    1946questions

    Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

共通の処理が複数間のクラスにある場合、どうするのが良い?

解決済

回答 7

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 2,358
退会済みユーザー

退会済みユーザー

※初期化時に渡すvalueは数字という前提です。

class AAA {

    constructor(value) {
        this.value = value;    
    }

    asValueSeparatedByComma() {
        return String(this.value).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
    }

    doSomething() {

    }

}


class BBB {

    constructor(value) {
        this.value = value;    
    }

    asValueSeparatedByComma() {
        return String(this.value).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
    }

    doSomethingElse() {

    }

}

例えば、addCommaという数字を3桁区切りでカンマをつける全く同じメソッドが複数間のクラスに存在する場合、
親クラスを作ってあげるとか、そのメソッドを別ファイルに保存してインポートして使うとか、いろいろと選択肢はあると思うのですが、
適切な方法の判断の仕方がわかりません。

とりあえず、同じ処理が複数の場所に散らばってしまうのは、いざ、その該当処理を修正する際に修正箇所が増えるので好ましくないというのは分かります。

継承を採用する場合、依存関係が親と子で生まれたり、あとは後で変更したいみたいになったときに、手間がかかりそうな気もしますし、いろいろと考えることもありそうです。関数を単にインポートして使うのは楽ですが、なんかユーティリティークラスはoop的には好ましくないみたいなことを目にしたこともあり、どうしたものかと。

というか、使ってるredux自体が関数型プログラミングを採用してる感じですし、その時点で関数インポートして使ってたりもしますし尚更わけわからん。

どんな方法が良いのでしょうか!!!!

 補足

https://www.google.co.jp/search?q=ユーティリティークラス+アンチパターン

 追記

数字を扱うクラスを作って、それをコンポジションして使えば良いんですかね?

 変更(2017/10/01 16:21)

カンマ区切りの処理がどういう処理か名前からわかりづらいというご指摘をmiyabiさんから受けたので、メソッド名を変更しました。

カンマ区切り用のメソッド引数に値を外から渡す書き方をしていましたが、意図していたものと違かったため、初期化時に渡される値をもとにカンマ区切りにした文字を返すように変更しました。

 追記(2017/10/01 16:45)

miyabiさんのprototype拡張コードをお借りしつつ、こんなの試しに書いてみましたが、「wrappedClass.prototype.value」は上手く行かないのですね。

class AAA {

    constructor(value) {
        this.value = value;    
    }

    doSomething() {
        console.log('hello');
    }

}


function enhancer(wrappedClass) {

    Object.defineProperty(
        wrappedClass.prototype,
        'asValueSeparatedByComma',
        {get: function(){return String(wrappedClass.prototype.value).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,')}}
    )

    return wrappedClass;

}

var enhancedClass = enhancer(AAA)

var enhancedObject = new enhancedClass(5)
console.log(enhancedObject.asValueSeparatedByComma); //undefined
enhancedObject.doSomething() //'hello'
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 7

checkベストアンサー

+8

mixinまたはトレイト

親子関係といった継承を使った関係ではないクラス同士で共通の処理がある場合どうするかというと、よくある言語ではmixinトレイトを使います。マイナーなaltJSの一つであるLiveScriptを使ったmixinの例を示しましょう。

HasValueSeparatedByComma =
  asValueSeparatedByComma: ->
    String(this.value).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');

class AAA implements HasValueSeparatedByComma
  (@value) ->

  doSomething: ->

class BBB implements HasValueSeparatedByComma
  (@value) ->

  doSomething: ->

a = new AAA(1234)
b = new BBB(5678)

console.log(a.asValueSeparatedByComma())
console.log(b.asValueSeparatedByComma())

asValueSeparatedByCommaの実装が一つにまとまっているのがわかると思います。では、これを生のJavaScriptで書くとどうなるのかというと…今はできません。なぜなら、現在のJavaScript(ECMAScript)でmixinやトレイトを機能として提供していないからです。mixinを提供するための機能としてFirst-Class Protocolsが提案されていますが、現在stage 1(stage 4が正式採用)であり、まだまだ道のりは遠い状態です。

ただ、機能として提供していないからと言ってできないわけではありません。上のLiveScriptですと、prototypeにforで回しながら全て突っ込むという荒技を使って実現しています。ES2015+で再現するとなると次のような感じです。

function importAll$(obj, src) {
  for (const key in src) obj[key] = src[key];
  return obj;
}

const HasValueSeparatedByComma = {
  asValueSeparatedByComma: function() {
    return String(this.value).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
  }
};

class AAA {
  constructor(value) {
    this.value = value;
  }

  doSomething() {
  }
}
importAll$(AAA.prototype, HasValueSeparatedByComma);

class BBB {
  constructor(value) {
      this.value = value;
  }

  doSomethingElse() {
  }
}
importAll$(BBB.prototype, HasValueSeparatedByComma);

a = new AAA(1234);
b = new BBB(5678);

console.log(a.asValueSeparatedByComma());
console.log(b.asValueSeparatedByComma());

これが良いのか悪いのかと問われると、微妙と思っています。そもそものやり方が悪いというよりも、JavaScriptはこういう設計が向いていない、こういう設計をできるように考えて作られていない、こういう設計をするには力不足である、という感じです。かつてJavaにもmixinがありませんでしたが、そのときと同じ匂いを感じています。

「ユーティリティークラスは悪」のように言われていますが、言語そのものの機能不足の所為で、ユーティリティークラスでも作らないと共通部分が書きにくいJavaの功罪だと思っています。それと同じで、まだクラスベースのオブジェクト指向をするには十分とは言えないJavaScriptにおいては、ユーティリティークラスでも良いんじゃ無いかと思っています。もし、それがどうしても嫌なら、altJSを使うしか無いでしょう。


上の話とは関係無い、別の方法を紹介します。善し悪しについては判断はしません。

意図的に汎用なメソッド

AAA.prototype.asValueSeparatedByCommaは意図的に汎用な作りですので、そのまま流用可能です。

class AAA {
  constructor(value) {
    this.value = value;
  }

  asValueSeparatedByComma() {
    return String(this.value).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
  }

  doSomething() {
  }
}

class BBB {
  constructor(value) {
      this.value = value;
  }

  doSomethingElse() {
  }
}

a = new AAA(1234);
b = new BBB(5678);

console.log(a.asValueSeparatedByComma());
console.log(AAA.prototype.asValueSeparatedByComma.call(b));

Number拡張

Numberそのものを拡張します。asValueSeparatedByComma自体がまとめられるわけではありませんが、複雑なコード部分を共通化できます。

Number.prototype.toStringSeparatedByComman = function() {
  return this.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
};

class AAA {
  constructor(value) {
    this.value = value;
  }

  asValueSeparatedByComma() {
    return this.value.toStringSeparatedByComman();
  }

  doSomething() {
  }
}

class BBB {
  constructor(value) {
      this.value = value;
  }

  asValueSeparatedByComma() {
    return this.value.toStringSeparatedByComman();
  }

  doSomethingElse() {
  }
}

a = new AAA(1234);
b = new BBB(5678);

console.log(a.asValueSeparatedByComma());
console.log(b.asValueSeparatedByComma());

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+5

 静的関数

また、「リファクタリングを求めているわけではないのでコードを書く必要性を感じません」といわれそうですが、ケースバイケースだと思うので、コードを出してほしいと思います。
コードを出せば、前提条件を後出しする事がないからです。

例えば、addComma() が静的関数であった場合、class AAA や class BBB に存在しなくても良いと考えられます。

function addComma (numberString) {
  return numberString.split(/(?=(?:\d{3})+$)/).join();
}

関数 addComma は class AAA, class BBB に依存せずに動作する為、そこに含めるべきではありません。

 汎用性

2017/10/01 16:21の変更を受けて追記します。
関数名が変更(addComma -> asValueSeparatedByComma) されていますが、そこは重要ではなく、addComma では存在した仮引数 value がなくなっています。
変更前の addComma では静的関数を予想させるコードでしたが、this.value に依存するメソッドへ変質したことで状況は大きく変わりました。

共通処理のメンテナンスコストを下げるには共通処理を外へ追い出す必要があります。
共通処理を外へ追い出すという事は依存関係が出来るという事です。
どうやっても依存関係が出来るのなら、「汎用性が高い方法」を採用するのがベストだと考えます。

一つは、参照透過性です。
ご質問のコードでは this.value に依存している為、参照透過性がないコードですが、this.foo でも this.piyo でも再利用できるコードが好ましいでしょう。

'use strict';
function insertCommaDelimiter (numberString) {
  return String(numberString).replace(/(\d+)(\.\d+)?/, function (subString, capture1, capture2) {
    capture1 = capture1.split(/(?=(?:\d{3})+$)/).join();
    return capture2 ? capture1 + capture2.replace(/(\d{3})(?=\d)/, '$1,') : capture1;
  });
}

class AAA {
  constructor (value) {
    this.value = value;
  }

  asValueSeparatedByComma () {
    return insertCommaDelimiter(this.value);
  }
}

class BBB {
  constructor (foo) {
    this.foo = foo;
  }

  asValueSeparatedByComma () {
    return insertCommaDelimiter(this.foo);
  }
}

console.log(new AAA(12345678).asValueSeparatedByComma()); // "12,345,678"
console.log(new BBB('私の所持金額は43421円です。').asValueSeparatedByComma()); // "私の所持金額は43,421円です。"

更に汎用性を上げると、挿入されるデリミタはカンマでなくても良く、任意の文字数単位でデリミタを挿入できる設計が考えられます。

function insertDelimiterByNumberString (numberString, digit, delimiter) {
  var digit = Number(digit),
      integerPart = new RegExp('(?=(?:\\d{' + digit + '})+$)'),
      decimalPart = new RegExp('(\\d{' + digit + '})(?=\\d)', 'g'),
      delimiter = arguments.length < 3 ? ',' : String(delimiter);

  return String(numberString).replace(/(\d+)(\.\d+)?/g, function (subString, capture1, capture2) {
    capture1 = capture1.split(integerPart).join(delimiter);
    return capture2 ? capture1 + capture2.replace(decimalPart, '$1,') : capture1;
  });
}

console.log(insertDelimiterByNumberString(12345678.42133212, 3)); // "12,345,678.421,332,12"
console.log(insertDelimiterByNumberString(180012, 2, ':'));       // "18:00:12"

もう一つの方向性としては、汎用的なクラスを作る事です。
例えば、moment.js は Date を操る事に特化したライブラリですが、カンマ区切りの数値を作る処理も何らかの汎用性の高いクラスの一部とします。
class AAA, class BBB は作った class との間に依存関係が出来ますが、汎用性が高いクラスであれば気にならないかもしれません。
ただし、この方法はたった一つの機能が欲しいがために重い class をインポートする事にも繋がるので、設計方針次第では採用しがたいと思います。

いずれにしても、再利用性を高める為には、可能な限り、汎用性が高い設計にする必要があると考えます。
私の書いた例は一例に過ぎず、最終的には「汎用性をどの方向に追求するか」というポリシーの問題になります。

 更新履歴

  • 2017/10/01 18:26 「汎用性」を追記

Re: hayatomo さん

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/01 16:24

    ご回答ありがとうございます。コード修正いたしました!

    キャンセル

  • 2017/10/01 18:27

    親記事に追記しました。

    キャンセル

+2

共通処理がオブジェクトの状態に依存するのであれば、親クラスとして汎化することができる場合もあるかと思います。そのような場合は継承を用いるとよいのではないかと思います。

そのような特性を持たない(例示の addComma のように value の値を加工して返却するのみ、等の)場合はユーティリティクラスとして static メソッドでの実装を行ってもよいのかと思います。
ユーティリティクラスは、1つのクラスにメソッドを詰め込みがちだったり、なぜそのクラスが処理を行うのかという意味付けがしづらかったりと扱いづらい面もありますが、すべての操作をオブジェクト化することのコストとの兼ね合いもありますので、一概に不可とする必要もないと思います。

余談ですが、共通で使用する可能性のあるメソッドや定数をベースとなるクラスに定義して、作成するクラスはすべてベースクラスを継承する、というつくりを見たことがありますが、プログラミング時になぜその機能が使えるのかの理解がすぐにできず、分かりづらいと感じました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/01 15:23

    ご回答ありがとうございます!「共通処理がオブジェクトの状態に依存するのであれば、」というのは、例えば、this.stateみたいなかたちでクラス内に定義されたメソッド内でインスタンスプロパティを直接参照しているような状態を指しますでしょうか?

    キャンセル

  • 2017/10/01 15:33

    ご認識の通りです。
    オブジェクトの文字列表現を返すtoStringなどは、全てのオブジェクトが備える性質としてObjectクラスに定義され継承されていますね。

    キャンセル

  • 2017/10/01 16:25

    ありがとうございます!意図したコードと違うコードを書いてしまっていたので、オブジェクトの状態に依存するかたちにメソッドを書き換えました。このような場合は継承が1つの選択肢として挙がってくるのですね。

    キャンセル

+2

例えば、addCommaという数字を3桁区切りでカンマをつける全く同じメソッドが複数間のクラスに存在する場合、
親クラスを作ってあげるとか、そのメソッドを別ファイルに保存してインポートして使うとか、いろいろと選択肢はあると思うのですが、
適切な方法の判断の仕方がわかりません。

それはクラスがおデブさんになるから嫌だなぁ…

例えば数値を3桁区切りのカンマに変換すると使い勝手が超絶悪いStringになるから、
出力の直前までNumberのまま利用して、Viewが画面表示に使う直前で初めて3桁区切りのStringになって欲しい。
そうなると、クラスではなく関数やプロトタイプ拡張に存在するのが正しい実装になる。

クラスやインスタンスは出来る事が少なければ少ない程良い。
責務を必要最低限に絞るからこそ、このクラスは何のために存在しているかがコードから一発で分かるわけで、
addCommaやらなんやらのどうでもいいメソッドがゴテゴテ付いた豪華なクラスだと存在意義が見えてこない。

さらにいうと、addCommaってなんだろう。
カンマを加えるのに成功したのかという結果(trueとfalse)を返すメソッドなの?
どうされたのかを表すならcomma_separatedの方が良さそう。
それを踏まえてコードにしてみたよ。

const vouchar = new Voucher(1050) // vouber: 領収書

// case1: メソッドに追加
console.log(voucher.commaSeparatedValue())
// 1,050

// case2: 関数定義
// commaSeparated :: Number -> String
const commaSeparated = it => it.toLocalString() // 横着した
console.log(commaSeparated(voucher.value))
// 1,050

// case3: Number.propertyに追加
Object.defineProperty(
  Number.prototype,
  'commaSeparated',
  {get: function(){return this.toLocaleString()}}
)
console.log(voucher.value.commaSeparated)
// 1,050

誰でも使うような普遍的な処理は、専門にしてる誰かにやってもらえばいい。
「テレビを見たくなったらリモコンを隠すアルバイト」みたいな仕事作ってその人に移譲すればいいわけだ。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/01 15:21

    ご回答ありがとうございます!画面表示前にカンマ区切りにした数字にしたいというのが、そのメソッドを必要としている理由です。
    case1は説明頂いた中にあったように、クラスがゴテゴテになるからオススメ出来ないというパターンでしょうか?(複数のクラスに同じメソッドを作ることにもなります。)

    case3はネイティヴオブジェクトを拡張するのは、他の開発者の混乱のもとだから控えた方が良いと本で学んだことはありますが、Numberオブジェクトにその処理を持たせるのは凄い自然に感じます!

    case2はデメリットが無いような気がしますが、case2がオススメでしょうか?

    キャンセル

  • 2017/10/01 16:00

    クラスを引数に取った関数つくって、その関数内で引数に渡したクラス拡張して、その拡張クラスのインスタンス返すみたいなのは、おかしいですか?
    出先でコード試せてないのでそもそも出来るのかすら未確認ですが。

    キャンセル

  • 2017/10/01 16:26

    ↑いまから試してみます。

    キャンセル

  • 2017/10/01 16:47

    ↑上手くいきませんでした。(質問文に追加)

    キャンセル

  • 2017/10/01 17:28

    case1はクラスがデブになるからあんまりよろしくない。
    case2は現実的な案だね。
    case3は追加するならネイティブのNumberオブジェクト一択。

    問題視されるのはPrototype.jsという汎用的なライブラリが、
    ネイティブのオブジェクトを汚しまくって大問題になったという歴史があるからね。
    その対策としてES5で実装されたObject.definePropertyのやり方で汚染を最小限に留める事が出来る。
    自分のプロジェクト内だけで、カンマ区切りとして使うなら干渉する事も少ないだろうし、まぁ大丈夫なんじゃないかな?

    キャンセル

  • 2017/10/01 17:32

    > クラスを引数に取った関数つくって、その関数内で引数に渡したクラス拡張して、その拡張クラスのインスタンス返すみたいなのは、おかしいですか?
    ES2015だと最初からgetterで書けるから意味なくない?

    キャンセル

+2

私の場合なんとなく同様のケースは無いだろうなと思いつつ、しばらく考えるのに時間がかかりましたがどういうことか思い当たりましたので書きます。読んでみるとOOPをそれほど大事に考えていないことが判ると思います。OOPが大事というなら的外れな回答になっている点をご了承下さい。

クラスの設計において、私の場合は3層に分けて設計します。

1つ目は、システム内部の足回りで、ファイルの入出力をしたり、DBの接続をしたりSQLを管理して永続化したオブジェクトの取り出しを行ったり、する部分です。泥臭い設定部分を受け持ちます。

2つ目は、外部へのインターフェイスでこれにはWebなどのUIも含まれます。多様なインターフェイスで同様の機能が提供出来るようにインターフェイスに依存する部分はここで受け持ち泥臭い編集などを行います。

で最後が純粋にオブジェクトで表現する層です。ドメインをオブジェクトの関係で表し、各オブジェクトが協調して動作します。これを行うために前の2層で泥臭い部分を除去します。

これら3つの層にはそれぞれインターフェイスがあり、どのような構成を取るかはその時々で考えます。

この形で、とるとカンマ区切りは外部へのインターフェイスの層にあり、この部分は必ずしもオブジェクト指向にこだわるべき場所では無いのでユーティリティクラスを使います。

Format.separatedByComma(aaa.Value);


(ユーティリティクラスのカンマ区切りメソッドを表示の直前で呼び出し)

もちろんraccyさんの例のようにNumberを拡張するのもありだとおもいます。しかしその場合も値を保持するオブジェクトの外で編集するため、AAAやBBBに編集された値を返すメソッドは作りません。


追記ー
共通部分を外部から呼び出すのが正しいという意味ではなく、場合によって選択すべきだというのが主張です。その点が分かりにくくなっていたので追記します。

実際のところカンマ区切りというのは単なる一例でしょう。しかし、他の内容でも同様で責務として正しいのか検討すべきだと思います。

手段としては、大きく分けて、継承(・ミックスイン)・移譲・呼び出し元の三種類(四種類)あると思います。後の方ほど疎結合になります。

カンマ区切りはモデルではなく表示部の問題なので呼び出し元の表示部のクラスか責務を受け取るべきです。
(また、表示用のモデル毎に表示形式を保持させるのは個人的にはちょっとOOに偏り過ぎだと感じます。画面クラスがまとめて管理すべきだとかんがえます。)


追記ー
質問者さんは退会されたようですが、「現場で役立つシステム設計の原則」を読んだので自分用のメモとしてその点を追記します。

「3章 業務ロジックをわかりやすく整理する」で、三層+ドメインモデルについて書かれていますが、質問にある部分は三層の中のプレゼンテーション層の関心事です。明確には書いてないようですが、三層はそれぞれパッケージが別に分けるのが自然で、また、同一のドメインモデルを扱っても対象になるプレゼンテーションの対象が異なる(例えばWebUIとWebAPI)ならば、それぞれ別のプレゼンテーション用のクラスを定義します。

それに対して、実際の表示の部分は移譲を使うといいのではないでしょうか。といっても、金額の表示であれば金額用の表示をする(ヘルパー)オブジェクトに値を渡して、CSSのクラスなり編集結果なりを返すということになると思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/03 11:39

    お礼が遅くなりましてすみません。ご回答ありがとうございます。補足の説明も参考になりました。最近、増田亨さんのシステム設計の本を読みまして、表示に関することもドメインモデルに持たせることを推薦していたので、ちょっといろいろ考えてしまってました。

    キャンセル

  • 2017/10/03 12:10

    ああなるほど、プリミティブ型を嫌うドメインモデルだとそうなるかもしれませんね。(OOに偏り過ぎというのは付け焼刃でドメインモデルを設計すると却って可読性・変更性に乏しいことになりそうだと考えるからです。付け焼刃=>私がやると)

    web画面やjsonやXMLなど対象が違えば表示形式も違うし、スマホ向けだったりすると対象はもっとシビアになります。実際は、複数の表示形式が必要なことは多くはありませんが、オブジェクトが肥大してドメインが簡潔に表現できない気がします。その本では点どうなっているのか私も興味深いです。

    今回の例は私はこうするという例で他の優れたやり方もあるとおもいます。実際書いたときは、表示部が少し泥臭くなりますが、ドメインに関する点が泥臭くなるより良いというのが私の考えです。

    増田亨さんの設計はいつも発見に満ちてます。年中に時間を作ってその本は読もうと考えていたところです。

    キャンセル

  • 2017/10/05 08:22

    その本では表示に関する加工、判定、計算の処理もドメインモデルに持たせることを推薦していましたが、前提として、表示先(web or スマホアプリ、コマンドラインなど)に依存する文字列はドメインモデルに持たせないとしてました。\nで区切られた段落は配列で段落をドメインモデル側では表現したり、cssをドメインモデルに持たせてaの場合は#000000と値を判定後に返すのではなく、クラス名を返すなど。

    キャンセル

  • 2017/10/05 08:27

    それと本には、ドメインモデルと画面の項目にズレがある場合は、表示用のクラスを作って、仲介させるみたいなことも書かれていたと思いますが、あくまで推薦するのはドメインモデル側に表示用の処理も持たせることだそうです。ドメインモデルと画面項目がズレてしまってることが好ましくないとのことだった気がします。

    キャンセル

  • 2017/10/05 09:37

    質問はカンマ区切りに限った話ではないのでしょうけど、カンマは例えばWebとCSVでは全く意味がちがってくるので、表示用クラスの責務だと思うのです。そして、表示用クラスは当然、画面または画面の要素毎に作られるので、質問で示した値を持ったクラス(AAA・BBB)は表示クラスのメンバーとして扱うのが適切だと思います。
    また、カンマ区切の機能は画面表示クラスに非常に関連が強いのですが、画面表示の親クラスの実装とするにはインスタンスとの関連が低すぎるのではないでしょうか。私はヘルパークラスの移譲の方がよいと考えます。(パッケージは画面表示クラスと同じ。)
    この辺りの話は、回答に含める内容だと思うので後で反映させます。
    早く、「現場で役立つシステム設計の原則」読みたい・・・

    キャンセル

  • 2017/10/06 16:57

    参考になります!ありがとうございます!ちなみに、今、本を移動中に読んでましたら、カンマ編集や千円単位の表示もドメインオブジェクトが持つべき加工ロジックの「候補」と書いてました。ビュー側でドメインオブジェクトで表現する論理的な状態を利用するといった考え方らしいです。

    キャンセル

+2

カンマを付与するって行為自体は、項目の設定によるから共通化は難しいですよ。

私なら、画面の項目に属性(3桁カンマ表示)を付与しておきますね。
画面の属性処理みたいなクラスに集めておけばよいのではないでしょうか。

クライアント側に処理を持たせれば、サーバの処理を分散でき、汎用性も増しますね。

少なくとも、処理の中でカンマを付与して、
純粋でないデータを渡すようなことはしたくありません。

追記
画面の項目または、モデルクラスの各プロパティに属性を付与すればいいです。
属性を付与していない言語であるなら、辞書みたいなのを作って管理しますかね。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/05 08:14

    ご回答ありがとうございます!
    spaなのでクライアント側の話になります。

    キャンセル

+2

要は、AOPをjavascriptで、という話だと思いますので、将来的にはデコレータで処理したくなる案件ですかね……?
きれいで読みやすいJavaScriptを書く デコレーターの基本を先取り - WPJ

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • JavaScript

    17083questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Node.js

    1946questions

    Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。