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

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

ただいまの
回答率

89.97%

グローバル変数を使用しない設計方法

解決済

回答 6

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,793

tarotarosu

score 111

前提・実現したいこと

今、下記のようなグローバル変数が定義されているとします。
このグローバル変数はプログラム中のいたるところで使用されます。
(例えばボタンがクリックされたらクリックされたボタンに応じてグローバル変数の値を書き換えたり、これらの値を使って計算したり、色々です。)
実現したいこととしましては、これらのグローバル変数を使わないような設計にしたいということです。

最近Backbone.jsを勉強し始め導入を試みているのですが、グローバル変数があらゆるViewのメソッド内で乱用されています。
これでは、疎結合化を図るBackbone.jsの理念(?)からは大きく外れてしまうのではないかと懸念しています。
例の場合だと、ファイルを分割したときにglobal_1が様々なファイルをまたいでいるので、保守性のことなどを考えるとこれではいけませんよね…

Backbone.jsに限った話ではないですが、様々な処理で変数を共有させたい場合、どのような設計方法がベストなのでしょうか?
あまり具体例がなくぼんやりした質問ですが何かご回答を頂けると助かります。

global_1;
global_2;
global_3;

追記

より具体的な例で説明します。
<li>をクリックしたらその<li>内のテキストを取得して、<button>がクリックされたら先ほど取得したテキストを表示する。
という処理をjQueryだけでは以下のように記述しました。

HTML

<div class="demo">
      <ul>
        <li id="list1">list1</li>
        <li id="list2">list2</li>
        <li id="list3">list3</li>
      </ul>
      <button type="button" id="button">Button</button>
    </div>

jQueryでの記述

$(function(){
  var listText;

  $('.demo li').click(function(){
    listText = $(this).text();
  });

  $('#button').click(function(){
    console.log(listText);
  });
});

これを私なりにBackbone.jsを使って書き換えると、以下のようなコードになりました。

テンプレート

<script type="text/template" id="text-template">
  <%- text %>
</script>

Backbone.jsでの記述

var listText;

//liのModel
var LiModel = Backbone.Model.extend({
  defaults: {
    text: 'list1'
  }
});

//liのView
var LiView = Backbone.View.extend({
  tagName: 'li',
  template: _.template($('#text-template').html()),
  events: {
    'click': 'getText'
  },
  getText: function(){
    listText = this.model.get('text');
  },
  render: function(){
    var template = this.template(this.model.toJSON());
    this.$el.html(template);
    return this;
  }
});

//ボタンのView
var ButtonView = Backbone.View.extend({
  el: '#button',
  events: {
    'click': 'showText'
  },
  showText: function(){
    console.log(listText);
  }
});

var liModel = new LiModel();
var liView = new LiView({model: liModel});
$('.demo').append(view.render().el);
var buttonView = new ButtonView();

このような処理をBackbone.js的に実装しようとしても、「listText」というグローバル変数を記述するような方法でコーディングしてしまいます。
この程度ならlistTextが存在しても何の問題もないのですが、実装したい機能としてはもっと複雑で、listTextが様々な処理に絡み、listTextのような変数もかなり増えてくることが予想されます。

Backbone.jsに関係なく、クラス間でうまいこと変数を受け渡しできればいいのかなーと思うのですが、良い実装方法が思い浮かびません…

追記2

liのViewがおかしかったので少し修正しました

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 6

+6

サンプルコードではよくわからないのですが、書き換えがあるグローバル変数は良いものではありません。
バグを作りやすくし、デバックをやりづらくします。

また、通常はグローバル変数は極力使わない方法で作られているので、多用されている環境というのは珍しいと考えていいです。

逆に考えると、世間のほかのソースコードを見ればどのように組めばよいか書いてあるということになります。

ということで、Backbone.jsの本やwebで特にサイトを実際に作ってみるようなものを一通り自分の環境で作ってみましょう。また、さらに2つ3つレベルが違うものに挑戦すればだいたいどのようなパターンが存在するかつかめるようになると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

checkベストアンサー

+2

一般的に、グローバル変数を使わない書き方をするということは、その変数は何らかのクラスオブジェクトが保持し、必要になったらそのオブジェクトから値をGetしてくることになります。変更が必要になったらSetですね。(ゲッター・セッター)
セッターには範囲チェックを入れたりもできるので、より安全に作れるということになります。
ただし、設計の初期段階から、そういうことを意識し注意しながら設計する必要があります。途中からグローバル変数をなくそうとすると、影響度が大きく中々大変な作業になります。
例えば、Commonクラスのようなものを作成してこのクラスはシングルトンにしておき、複数のオブジェクトから参照や変更が必要な変数はこのCommonクラスが全て保持するような書き方をします。
ただ、Commonクラスの中に保持する変数のスコープはプラベートにしておかないと、クラス変数にした意味がありません。

グローバル変数は、全てではありませんが、設計やインターフェースを見直せば無くせるものも多いはずです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

>このグローバル変数はプログラム中のいたるところで使用されます。 

のだからグローバル変数を利用するのが正しいと思いますが
どうしても気になるなら、
所詮javascriptのグローバル変数はユーザーによって改ざんされる可能性はあるので
クッキーやinput type=hiddenなどでタグのvalueとして保持し、都度参照・更新すれば
よいかもしれません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

色々なケースがありますのでこれが正解!と言うのはありませんが、一般にはグローバル変数は悪とされてますね。
個人的にはグローバル変数で困った事はほとんどありませんが使わないようにしてます。
で、どのような?という、問いに対しては、メイン的な処理でローカル宣言して、パラメータで渡す。と言うのが、昔の一般的な考えかと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

backbone.js を利用したことがないので、まちがっていたらごめんなさい。
backbone.js は MVC のフレームワークなので、編集中の値などは model に保持するのが正しいのではないでしょうか。 model内で自分の好きなように構造化し、その構造は view の構造と独立に設計できることが MVC のメリットだと思います。 MVC を使っている以上、グローバル変数は不要なのではないでしょうか?

それで、

getText: function(){
  listText = this.model.get('text');
},

このコーディングはやろうとしていることとあっていますか?
li をクリックしたらそのテキストの値を model にセットするべきなのでは?
click イベントの場合、引数に要素を受け取れるのではないでしょうか。それを this.model.set で格納し、button のイベントでは、そのモデルの値を this.model.get で参照すればよいのではないでしょうか。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

複数の機能間でデータを共有する方法は handleEvent, data-*属性、カスタムイベント、WeakMap 等があります。

 handleEvent

<div class="demo">
  <ul>
    <li id="list1">list1</li>
    <li id="list2">list2</li>
    <li id="list3">list3</li>
  </ul>
  <button type="button" id="button">Button</button>
</div>
<script>
'use strict';
document.querySelector('.demo').addEventListener('click', {
  text: '',
  handleEvent: function handleClick (event) {
    var target = event.target;

    if (target.tagName === 'LI') {
      this.text = target.firstChild.data;
    } else if (target.id === 'button') {
      console.log(this.text);
    }
  }
}, false);
</script>

 data-*属性

要追記。

 WeakMap

要追記。

 カスタムイベント

要追記。

Re:  kerokero335 さん

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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