信頼はずっと、挑戦はもっと。

お問い合わせ
TEL:03-3496-3888

BLOG コアテックの社員ブログ (毎週月曜~金曜更新中)

LIST OF ARTICLES

記事一覧

  • 画像:ブログサムネイル

    テクログ

    オブジェクト指向は因数分解だ!!

    どうも!先月に入社二年目を迎え、とうとうパイセンと化したわいです。自粛期間中ということで、かなり暇なので長編に挑みます。暇な人はぜひ最後まで読んでみてください。「うわあ、暇だあ…」ってより実感すると思います。今回、またもやプログラミングを他の概念にこじつけるシリーズです。この一年間、新人プログラマーとしていろいろ苦戦しました。その中のひとつが、『オブジェクト指向とは』の理解です。オブジェクト指向に関する記事はたくさんあります。それこそ、「オブジェクト指向はもう古い」とされていたりします。ただ温故知新という言葉があるように、オブジェクト指向について学ぶことは決して意味のないことではないと思います。むしろ、プログラミングをする上での基本的な考え方が詰まっているように思えます。詳しく、そして正確に知りたい場合は、他の記事をおすすめします。https://www.google.com/search?q=%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%81%A8%E3%81%AF今回はオブジェクト指向のイメージを掴むために少しでもお役に立てればと思い、あえて変な角度からアプローチしてみます。その名も、「オブジェクト指向は因数分解だ!!」です。因数分解とは、「足し算・引き算で表されている数式をかけ算の形に変形する」ことです。例えば、cx^2+acx+bcx+abc = (x+a)(x+b)c となります。(中学数学でやりましたね?)ここで、下図のようなWebページについて考えてみましょう。(私自身、最初に車の例えを見たときピンとこなかったので…)犬の情報は、売られている犬がリストで表示されているとします。猫も同様です。店舗の情報は、住所、電話番号などが表示されているとしましょう。(ちなみに、私はペットショップに行くと悲しい気持ちになるので、基本的に中には入りません)かなり簡略化しているのでわかりやすいと思いますが、ペットショップのWebサイト = 犬の情報 × 猫の情報 × 店舗の情報という式が成り立つはずです。複雑なWebサイトでもこのように各パーツの組み合わせで成り立っています。これを先ほどの因数分解と合わせて考えてみます。ペットショップのWebサイト= cx^2+acx+bcx+abc (一見複雑だが…)=  (x+a)  ×  (x+b)  ×  c = 犬の情報 × 猫の情報 × 店舗の情報このようにWebサイトを因数分解することができました。「だから、なんだ?」という話なのですが、ここでオブジェクト指向について考えたいと思います。なぜわざわざ小難しいオブジェクト指向で書くかというと、それは変更に対して柔軟に対応するためです。どういうことかというと、「爬虫類も売ることにしたから、サイトに爬虫類の情報も表示したい」や、「犬を売るのをやめて、鳥を売ることにしたから変えてくれ」などという要望に、瞬時にそしてバグを出すことなく対応することがオブジェクト指向を用いると容易になるということです。適切にオブジェクト指向を使えば、似たような機能の追加が容易になり(再利用性)、たった一か所の変更のために他のパーツにバグが出ていないかページ全体をテストし直す必要がありません(疎結合性)。しかし、このオブジェクト指向の難しいところはどのように機能(クラス)を分割するかというところにあると思います。大雑把に分割しても効力は発揮しないし、かといって細かすぎても保守が大変になります。ここで、今回私が提案するのは「因数分解のように考えてみたら意外と上手くいくんじゃね?w」ってやつです。まず、「犬の情報」を「鳥の情報」に変えるということを因数分解で見てみましょう。これは、仮に a → d とすることで実現するとします。そうすると、ペットショップのWebサイト=  (x+d)  ×  (x+b)  ×  c = 鳥の情報 × 猫の情報 × 店舗の情報となり、(x+b)やc の値に何ら影響を与えることなく変更することが出来ました。ここで勘のいい人は、「おめえさん、x を変えたら『猫の情報 (x+b)』に影響を与えるだら?」と思うかもしれません。そうです。そして、この x がオブジェクト指向の要です。「犬の情報」も「猫の情報」も、『売られている動物がリストで表示される』という共通点があります。この共通点を規格として、インターフェースや抽象クラスにまとめ上げることで再利用性が高まり、さらに各々のクラスの役割がはっきりします。そして、ここで注意しないといけないのがなんでもかんでもこの x にまとめてしまうことです。各ペットの情報欄に年齢や性別のほかに、仮に「1日に〇回散歩に行かないといけない」という項目があったとします。これを x にまとめてしまうと『犬の情報 (x+a)』ではいいんですが、関係のない『猫の情報 (x+b)』に影響を与えてしまいます。だから、この場合は各ペットの情報欄の項目までは共通化せず、『売られている動物がリストで表示される』という根本機能だけを共通化することがいいように思えます。(犬と猫だけだと差異がわかりにくいですが、変わった動物を追加するとなると変な項目も増えそうです)かといって、ペットショップのWebサイトということなので、『売られている動物がリストで表示される』という機能の共通化を疑う必要はないでしょう。それだと、オブジェクト指向の意味がなくなります。次に、f(a) = x+a とすると、ペットショップのWebサイト=  f(a)  ×  f(b)  ×  c = 犬の情報 × 猫の情報 × 店舗の情報となり、 犬(a)  や  猫(b)  を f に与えるだけで欲しい情報が得られるということです。この f こそが、『売られている動物をリストで表示する』という機能であり、インターフェースです。この『動物』という抽象に対してプログラミングしていくことが、ポリモーフィズムです。最後に、カプセル化について考えます。f はインターフェースや抽象クラスで、 f(a) は『売られている犬をリストで表示する』という具象クラスです。実際にペットショップのWebサイトを構築する際は、この f(a) を使います。そして、この f(a) を利用するとき、『売られている犬をリストで表示する』という具象クラスであるということだけを知っていればよくて、f(a) = x+a だという具体的な中身を知っている必要は全くありません。実は、『売られている犬を「人気順に」リストで表示する』ということをやっていて、中身が f(a) = 3x+1+a と多少複雑になっているかもしれません。ただ、このように具体的な中身を知らずとも、 f(a) を使えばWebサイトは構築できます。クラスを外部から利用するとき、 f(a) のような簡素な見た目で提供し、影響範囲をクラス内にとどめておくことをカプセル化といいます。いかがでしたか?「オブジェクト指向は因数分解だ!!」理論は。あなたのソース、c(x^2+ax+bx+ab) や x^2(1+a/x)(1+b/x)c になっていないでしょうか?「うわあ、暇だあ…」最後まで読んでくださり、私としてはうれしい限りですが、相当お暇なんですね。なにか自粛期間にできる趣味をお探しになることをお勧めします。以上、わいでした。健闘を祈る!!
  • 画像:ブログサムネイル

    テクログ

    serverless frameworkで API GatewayのプライベートAPIを作成したい!

    プライベートAPI、便利ですね。内部でいろいろやるだけなのに外に出したくないですし。プライベートAPIをAPIGatewayで建てる場合、手動での作成や、既存で建ててあるAPIGatewayにリソースを追加することも可能ですが、serverless frameworkでlambdaのデプロイと同時にAPI GatewayのプライベートAPIを作成したいですよね。なのでやってみました。全体的にはhttps://www.serverless.com/framework/docs/providers/aws/events/apigateway/やhttps://github.com/serverless/serverless/pull/5080にあるのですが、いろいろ摘んで探していかないといけないので書いておきます。serverless.ymlが、このようになります!最低限くらいの内容です。service: internal-apigateway-test provider:   name: aws   endpointType: private   resourcePolicy:     - Effect: Allow       Principal: '*'       Action: execute-api:Invoke       Resource: arn:aws:execute-api:ap-northeast-1:<AWS account ID>:* #リソースにしぼってもいいです       Condition:         StringEquals:           'aws:sourceVpce': vpce-<あらかじめ作成してあるVPC Endpoint です>   runtime: python3.8   region: ap-northeast-1   role: ${opt:role_str, "arn:aws:iam::<AWS account ID>:role/aaaa"} functions:   main:     handler: handler.lambda_handler     name:  internal-apigateway-test     description: "(sample)内部APIGateway+簡単なjsonを返すだけlambdaの作成"     memorySize: 128     timeout: 5     events:       - http:           path: /           method: get ではよいslsライフを!
  • 画像:ブログサムネイル

    テクログ

    wafのサンプルログを定期的にとって、あったら自動でchatworkに通知してほしいというきもち

    どうも、相変わらずいろいろ検証をしています。RDS Proxy はやくGAになってほしいですね。さて、標題の気持ちになったのでLambdaを書きました。ファイル分け?大丈夫大丈夫このくらいなら。冗長だとしてもわかりやすさ優先なのはいつもどおりです。弊社ではcloud9でのserverless frameworkでのデプロイも普通になってきましたね。Lambdaがはかどりますね。さてソースです。こちら、参考URLです。https://dev.classmethod.jp/articles/get-aws-waf-sample-logs/こちらを色々変更し、指定したwaf 指定したルールを 指定した間隔で行うようにしています。環境変数に入るだけ何個でもいけますね。あ、これはCF版ですが、LB版は上のクライアントを変えるだけだと思います。(作って運用しています。増えすぎるのであえてCF版とLB版を分けました)実行はcloudwatch eventsで設定です。slsの設定で。環境変数はこんな感じCHATWORK_API_TOKEN XXあんごうかしたトークンだよXXCHECK_LISTS [{'Name':'ばいたいめいだよ','WebAclID':'aaaa-aaaaとかだよ','WAFName':'わかりやすくするためのwafの名前です','RuleID':'aaaa-bbbbとかのルールID','RuleName':'これも基本わかりやすくするためのルール名'},]INTERVAL 11(とか数字)ROOM_ID 11111111(とかのルームID)# -*- coding: utf-8 -*- #cfのwafのサンプルログを確認し、あれば通知する import os import urllib from datetime import datetime, timedelta import boto3 import sys from base64 import b64decode import ast REGION = "ap-northeast-1" #環境変数 #Chatwork API トークン ENCRYPTED_CHATWORK_API_TOKEN = os.environ['CHATWORK_API_TOKEN'] CHATWORK_API_TOKEN = boto3.client('kms').decrypt(CiphertextBlob=b64decode(ENCRYPTED_CHATWORK_API_TOKEN))['Plaintext'] INTERVAL = int(os.environ['INTERVAL']) ROOM_ID = os.environ['ROOM_ID'] CHECK_LISTS = ast.literal_eval(os.environ['CHECK_LISTS']) #client用意 CF版だよ waf = boto3.client('waf') #### # replace datetime to string #### def time2str(x):     x['Timestamp'] = x['Timestamp'].isoformat()     return x #### #chatworkに通知 #### def postChatwork(message, room_id):   END_POINT_BASE = "https://api.chatwork.com/v2"   END_POINT      = "/rooms/"   ACTION         = "/messages"      headers = { 'X-ChatWorkToken': CHATWORK_API_TOKEN }      data  = { 'body': message }   data = urllib.parse.urlencode(data)   data = data.encode('utf-8')      # リクエストの生成と送信   post_message_url = END_POINT_BASE + END_POINT + room_id + ACTION   request = urllib.request.Request(post_message_url, data=data, method="POST", headers=headers)   with urllib.request.urlopen(request) as response:     response_body = response.read().decode("utf-8")     print(response_body) #### #dictを文字にするだけ #### def makeText(marge_logs):   text = "[info][title]CF版 WAF 検知ログ 直近"+str(INTERVAL)+"分[/title]"      for log in marge_logs:     for key, value in log.items():       text += key + "    " + value + "\n"     text += "[hr]"        text += "[/info]"        return text ### #main ### def lambda_handler(event, context):   #LISTごとに繰り返し   for CHECK_LIST in CHECK_LISTS:     WebAclId = CHECK_LIST['WebAclID']     acl = waf.get_web_acl(WebACLId=WebAclId)          WebACLName = acl['WebACL']['Name']        marge_logs = []     for rule in acl['WebACL']['Rules']:       RuleId = rule['RuleId']              if RuleId == CHECK_LIST['RuleID']:         # get sample requests         r = waf.get_sampled_requests(           MaxItems=3,           WebAclId=WebAclId,           RuleId=RuleId,           TimeWindow={             'EndTime': datetime.utcnow(),             'StartTime': datetime.utcnow() - timedelta(minutes=INTERVAL)           }         )              # check sample count         if 'SampledRequests' not in r or len(r['SampledRequests']) == 0:           #ログのみ           print("WebAclId:" + WebAclId + " RuleId:" +RuleId + " サンプルカウントなし")         else:           # replace datetime to string           logs = list(map(time2str, r['SampledRequests']))           for l in logs:             headers_dict = l['Request']['Headers']                      Host = ''             From = ''             UserAgent = ''             UserAgent2 = ''             Referer = ''             Referer2 = ''             for hd in headers_dict:               if hd['Name'] == 'Host':                 Host = hd['Value']               elif hd['Name'] == 'From':                 From = hd['Value']               elif hd['Name'] == 'User-Agent':                 UserAgent = hd['Value']               elif hd['Name'] == 'user-agent':                 UserAgent2 = hd['Value']               elif hd['Name'] == 'Referer':                 Referer = hd['Value']               elif hd['Name'] == 'referer':                 Referer2 = hd['Value']                          simpledict = {               '媒体名':CHECK_LIST['Name'],               '時間':l['Timestamp'],               'IP':'https://ipee.at/'+l['Request']['ClientIP'],               '国':l['Request']['Country'],               'URI':l['Request']['URI'],               'Headers.Host':Host,               'Headers.From':From,               'Headers.User-Agent':UserAgent + UserAgent2,               'Headers.Referer':Referer + Referer2,               '対象WAF/ルール':CHECK_LIST['WAFName']+' / '+CHECK_LIST['RuleName'],               'Method':l['Request']['Method'],             }             marge_logs.append(simpledict)               # chatwork通知           if marge_logs != []:             try:               print(marge_logs)               cw_text = makeText(marge_logs)               postChatwork(cw_text, ROOM_ID)             except Exception as e:               print(e)           else:             postChatwork("ログないよ", ROOM_ID)#基本ここはないですね ではよいWAF生活を!
  • テクログ

    ルーター異常? 調査記録

    現在グループ会社でDHCPが以上に使われる現象が起きています。ルーターでDHCPの枠は130ありますが1日経つと空きが0になっています。wifiのせいかと思い別回線に移動させており今まで圧迫していたIPを開放しました。現在社内のIPは固定化しておりDHCPの設定外にあるので足り無くなるはずはないのですが。またVPNソフトを使い外部からIPを取ってはいますが、20人もいないはずなので0にはならないはず。まとめ・社内は固定IPに設定・wifiはDHCPだが別回線で繋いでいるのでメイン回線には関係がない・VPNでのDHCPは20人程度・社内でテストなどDHCPを使う事があっても100は埋まらない上記を踏まえて今後の点検作業としては・ルーターのDHCP開放がうまくいっていない・管理者が把握していないDHCPを発行する機器がある(個人wifiなど)ますは以上2点に絞り機器の点検を行う予定です。
  • テクログ

    Cloudwatch eventsでcronを使って 日か、曜日を指定するとき

    ひなっちです。Cloudwatch eventsでcronなんですが、地味に忘れて、ハマるんです。なぜか?普段、Linuxのcronタブに書いてる書き方とすこしちがうからです。Parameter ScheduleExpression is not valid. ↑これcron的には書き方正しいはずなのになーと今回、自分が少しはまったのが毎週金曜日、10時(JST)で処理を動かす場合でした。これを、そのまま素直にcronで書くと0 1 * * FRI * こうです。が、正解は0 1 ? * FRI * こうです。これ、ちゃんとマニュアルにも書いてあるんです。cron 式の日フィールドと曜日フィールドを同時に指定することはできません。一方のフィールドに値 (または *) を指定する場合、もう一方のフィールドで ? (疑問符) を使用する必要があります。https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/ScheduledEvents.html#CronExpressionsほんと地味ーーーにはまるし、普段cronタブに慣れ親しんでいるとマニュアル読むほどのものじゃない気もして、辿りつくまでに時間がかかりました。。。。では!
  • テクログ

    新人プログラマーが仕事を振られた時にやった方がいいと思うこと =後(プログラム新規作成編)=

    こんにちわ。パグと申します。前編はこちら→https://core-tech.jp/blog/article469/プログラム改修編はこちら→https://core-tech.jp/blog/article473/私はWEBサイト制作においては、プログラム改修よりもプログラム新規作成の方が、作業ハードルが低い様に感じます。理由としては、既存システムの影響範囲調査があまりないためです(新規だから影響箇所が少ない!)。既存システムに新規にページを作成する様な作業が、特に楽な作業に感じます。一方で、全て1から作らなくてはならないので、ルーティン化されていないときついのでは、と感じます。もしあなたが新卒、新人1〜2年目で、ページの新規作成を命じられた場合に何からしたら良いのかわからなくなってしまったら、「詳細設計」を順番に行ってみてください。1.自分の作成予定ページの動線を知る自分が作成するページはどこから入ってくるのかを理解します。表示画面がPOSTでくるのか、GETでくるのか、トップページなのか複数ページなのか、検索結果ページなのか・・・色々なパターンがあるかと思います。そして、入力、確認、完了の様な登録系の処理なのか・・・検索、検索結果の様な表示系の処理なのか・・・を整理し、自分の画面の遷移図を書いてみます。遷移図には書き方が色々ありますし、お作法があるチームもありますので、何も参考になる資料がない場合はもはや手書きでも良いと思います。例えば、簡単に書くとこの様な形になります。・遷移元 → 入力(新規、または更新) ←→ 確認 → 完了・遷移元 → 検索 → 検索結果 → 詳細ページ2.自分の画面の対象となるデータを知る登録系画面の場合は、データを登録するTBLの設計を行います(基本設計が終わってから作業を依頼される場合は、すでに終わっていることも多いフェーズです)表示系画面の場合は、取得元を調査します。ここでPGがすることはSQLを先に考えてしまうことです。Model設計(すでに存在する場合は、メソッドの設計)です。例えば以下のようなことを考えます。・新規登録の場合はどんなカラムが必要か・更新処理ではどのカラムを更新するのか・取得処理の場合はリレーションや、取得条件などは何か、何をInとして、何をOutしたら良いのかSQLが苦手という方によく出会います。DBによって一回でとるのか分けてとるのか、SQLの書き方も様々ですが、取得系処理のSQLが苦手な人は、どんな結果になっていて欲しいのか完成系のTBLを想像してからSQLを書くと頭が整理されるのではないかと思います。まずはSELECTで取得するカラムを想像します→次にWHEREやJoinの条件を考えます。3.機能の設計を行います。ここに関しては改修編と同様ですが、とにかく細分化すること。自分が書けると思われる単位までの細分化です。この様な形で大枠を決めたら、とにかく作業を細かい単位に分割していきます。詳細設計とか言われたけど、何からしたらいいんだ・・・ってなる人は、イベント単位で考え始めると、楽かな、と思います。クラサバ型にせよ、スタンドアロン型にせよ、イベントがあって処理があるので・・・何から書き始めたらいいの?と設計に混乱する人は、とりあえずその機能のイベントトリガーが何かを考えて、そのイベントに対する処理を考えるのが良いかと思います。バッチ処理なら、呼び出された時、画面処理なら、ユーザーの動作や表示されるタイミングです。・初期表示 ・Analyticsやメタタグなども確認する・POSTまたはGETでデータ受け取り・データがある場合→編集画面・データがない場合→新規画面・データのValidation・加工処理→エラーの戻し方・画面の遷移・画面の表示の仕方・Xを押下・・・・Xを変更・・・このようにイベントが大枠で、そこからの細分化を考えるとスムーズかなと思います。ちなみに、単体テスト仕様書もこの様な形で書くとわかりやすくなります。4. 詳細の設計ができたらコードを書きはじめます順番に作っていきましょう。表示させて、HTMLを作って、中身を書きます。・ルートを作る→URLを打ち込むと、空のController,Viewが表示される様にする・ViewにHTMLを書き込む→この時呼び出すCSS,JSなどを考えます・Conotllerを書き込む→機能設計通りにかく→Modelとつなぐ5.詳細設計を修正し、テスト仕様書を書くこの辺りも改修と同じく、テスト仕様書を書きながら、想定通りになっていない箇所を修正していきます。自分で、正常、異常系など含めてテストが一周できるくらいになっていればCD完了です。この時に、想定外エラーやXSS対策、CSRF対策などできていたらより素晴らしいです。業務システム系の開発の場合はCD完了って言ったのに、バグが出てる状態でテストチームに回したりするとめちゃくちゃ怒られます。最後に新人の人が初めての新規作成を命じられて、いきなり放置プレイする現場にはなかなか出会ったことがありませんが、何も言われないからといって「これでいいのかな?」と思いつつ進めるのは、オススメしません。・遷移図、TBL設計、機能設計ができたら、PM(PL)に確認する・一つのActionができたところでPM(PL)に確認するといった感じに、確認しながら進めていくと、完成後大幅に戻されることが少ないのではないかと思います。プロジェクトリーダーも、プロジェクトマネージャーも作業を振っている以上は仕様確認を何度もされることに、嫌な顔をしないはずなので、「これでいいのか」と何度も確認しながら作業していきましょう。口頭ベースだと確認に応じてくれない、リーダーの時間が取れないことも少なくはありませんが、質問の方式を変えてみると良いのではないでしょうか。例えば、表にまとめて、まとめて見てもらうとか、設計書のXXのXXが・・・と資料を渡すとか、色々方法はあります。工期に余裕のある現場であれば、先にテスト仕様書(コード)を作成してから、CDを行うとより精度を高めることができますので、オススメです。「新人の時にすればよかったこと」は、きっと3年経ってから気付くことが多くの人にあるかと思いますが、それを初期の段階でできていると、きっとこの先の自分が楽になることでしょう。以上です。ではまた。