インフラエンジニアの教科書読んでみた
インフラエンジニアの教科書を読んでみました。
せっかくなので個人的なレビューをしてみます。
ざっくりどんな本か
- インフラエンジニアの仕事の種類
- インフラエンジニアの基礎知識
といったものが記載されています。
良かった点
インフラの基礎知識、通信の知識など非常に重要な箇所をざっくりと網羅しています。
インフラってなんだろう?って状態で読むと、どんな概念があるといったとっかかりを得るのに非常に役に立つでしょう
僕も5年程度エンジニアをしているのですが、そういえばふわっとしか認識してなかった。。。みたいな知識はちょくちょく出てきました。
学び直す良いきっかけを得ることができました。
悩ましかった点
すべての記載が非常にざっくりで詳細はほぼ一切記載されていないことです。
この本に記載されている知識はこの本を読んでおけばOKというより、この本でその知識があるのを知るといった感じですね。
悪い点かと言われれば微妙な感じです。そういう目的の本なので
また、IntelCPUの種類一覧など載っていましたが、これは載っていてもすぐ古くなるので。。。みたいなのが散見されました。
こういう人に向いている
インフラのことを何も知らない人向けです。
ちょっとエンジニアやってたら勉強せざるを得ない必要な概念の詰め合わせといったところですね。
数年エンジニアをやっている方が学び直すためというのにはおそらく不十分でしょう。
インフラを何も知らない方は何がわからないかわからないといった方が多いでしょう。そういう方には神のような本だと思います。
Pythonで学ぶデザインパターン入門 Factory Methodパターン
結城先生の Java言語で学ぶデザインパターン入門
のコードを Python に書き直していくシリーズ第4段です。
Factory Methodパターンとは
前回紹介した
を、インスタンス生成の際の肉付けという特定パターンで活用したものです。
つまり、サブクラスでインスタンスを生成します。
ソースコード
コードの概要
ざっくりと言うとIDカードを作成するといったコードになります。
スーパークラスに、frameworkパッケージとしてインスタンス生成を担当する Factory
クラス と生成される Product
クラスが存在します。
それぞれにスーパークラスをIDカードにおける肉付けをして継承したクラスがidcardパッケージとして存在します。
ソースコード
Product
# coding: utf-8 from abc import ABCMeta, abstractmethod class Product(metaclass=ABCMeta): @abstractmethod def use(self): pass
Factory
# coding: utf-8 from abc import ABCMeta, abstractmethod from factory_method.framework.product import Product class Factory(metaclass=ABCMeta): def create(self, owner: str) -> Product: p = self.create_product(owner) self.register_product(p) return p @abstractmethod def create_product(self, owner: str) -> Product: pass @abstractmethod def register_product(self, product: Product): pass
インスタンスを返すメソッドだけ決め打ちで処理を生成してしまって、あとはサブクラスに任せます。
IDCard (Productを継承)
# coding: utf-8 from factory_method.framework.product import Product class IDCard(Product): def __init__(self, owner: str): print('%sのカードを作ります。' % owner) self.__owner = owner def use(self): print('%sのカードを使います。' % self.__owner) def get_owner(self) -> str: return self.__owner
IDCardFactory(Factoryを継承)
# coding: utf-8 from factory_method.framework.factory import Factory from factory_method.framework.product import Product from factory_method.idcard.id_card import IDCard class IDCardFactory(Factory): def __init__(self): self.__owners = [] def create_product(self, owner: str) -> Product: return IDCard(owner) def register_product(self, product: Product): product.__class__ = IDCard self.__owners.append(product.get_owner()) def get_owners(self) -> list: return self.__owners
Factoryを継承したこのサブクラスで具体的にどのクラスのインスタンスを作成するかを指定します。
実行ファイル
# coding: utf-8 from factory_method.idcard.id_card_factory import IDCardFactory if __name__ == '__main__': factory = IDCardFactory() card1 = factory.create('さるへい') card2 = factory.create('へいさる') card3 = factory.create('たかし') card1.use() card2.use() card3.use()
実際どう便利か
ポイントは、Factoryはサブクラスで具体的にどのインスタンスが生成されるかはわからないということです。
Factoryクラスはサブクラスの存在を一切気にする必要がありません。よってこの抽象クラスを使い回すことができます。
インスタンス生成の定形パターンがあった際などは、Factory Methodパターンを用いてコードの重複を排除できたりするのでおすすめです。
クリーンアーキテクチャ(Clean Architecture)読んでみた
最近巷で有名なClean Architectureを読んでみました。
ものすごく僕個人の気持ちですが、まとめてみました。
ざっくりどんな本か
といったことをアンチパターンと一緒に解説している本です。
良かった点
- オブジェクト指向や様々な原則の復習になった
- それがわからないとこの本の内容がわからないため
- 納得感のある話だった。
この本は、様々な原則に則ってかかれてあり、だいたいの理屈には納得感があります。
そもそも何が良いものか?みたいなものを理解するために オープン・クローズドの原則
など、様々な原則の復習が必要です。
この本にはその解説も併記されているのでその点ではわかりやすくかつとても復習になります。
悩ましかった点
- あまりにも断定口調
- 筆者の癖が強そう
この本の記述は、この理屈こそ正義みたいな感じが強いです。
この理屈から外れたら悪!みたいな断定が多く、過去のアンチパターンの紹介とともに、あんなアンチパターンを選択したあいつらは理解出来ない!みたいな記載がとても多いです。
ビジネスの都合場、一旦アンチパターンを選択せざるを得ないことは往々にしてあるでしょう。それすら悪と断定されてしまうのは微妙な気がします。
まとめ
とても断定口調なところ以外はとても良い本だと思います。
わかりやすい本なのでじっくり読んでみて、よく考えながら飲み込むと良いのではないかなぁと思います。
逆に自分があんまりない方だと流されていって良くない未来が見えそうな感じもします。
癖の強さまで継承されてしまうと、正直とても仕事上扱いづらい方そうになりそうだなぁという気持ちが強いですね。
Pythonで学ぶデザインパターン入門 Template Methodパターン
結城先生の Java言語で学ぶデザインパターン入門
のコードを Python に書き直していくシリーズ第3段です。
Template Method パターンとは
文字通り、処理の大枠のテンプレートを作るパターンです。
具体的には、抽象クラスなどで処理のテンプレートを作成し、その詳細な実装は継承したクラスに任せるといったものになります。
ソースコード
コードの概要
「文字や文字列を5回繰り返して表示する」プログラムです。
抽象クラスである AbstractDisplay
に display
メソッドが定義されており、それを叶えるために3つのメソッドが定義されています。
そのメソッドの中身は何も定義されておらず、その処理をサブクラスに任せるといったものになります。
つまり、その処理次第で多種多様な display
メソッドが構築できるといったことになります。
ソースコード
AbstractDisplay(処理のテンプレートが記載してある抽象クラス)
# coding: utf-8 from abc import ABCMeta, abstractmethod class AbstractDisplay(metaclass=ABCMeta): @abstractmethod def open(self): pass @abstractmethod def print(self): pass @abstractmethod def close(self): pass def display(self): self.open() for i in range(5): self.print() self.close()
概要通りのコードですね。
display
メソッドだけが具体的に記載されています。
それ以外のメソッドをサブクラスで実装しましょう。
CharDisplay(文字単体をdisplayする)
# coding: utf-8 from template_method.abstract_display import AbstractDisplay class CharDisplay(AbstractDisplay): def __init__(self, ch: str): if len(ch) > 1: raise TypeError('文字は一文字') self.__ch = ch def open(self): print('<<', end='') def print(self): print(self.__ch, end='') def close(self): print('>>')
H
を入力としたら <<HHHHH>>
といった出力があることを期待するものとなります。
抽象クラスのものを素朴に継承してるだけですね。
Pythonのprintは勝手に改行されてしまうので、改行しないようには一工夫必要です。
StringDisplay(文字列をdisplayする)
# coding: utf-8 from template_method.abstract_display import AbstractDisplay class StringDisplay(AbstractDisplay): def __init__(self, string: str): self.__string = string self.__width = len(string.encode('utf-8')) def open(self): self.print_line() def print(self): print('|%s|' % self.__string) def close(self): self.print_line() def print_line(self): print('+', end='') for i in range(self.__width): print('-', end='') print('+')
Hello World.
を入力とすると
+------------+ |Hello World.| |Hello World.| |Hello World.| |Hello World.| |Hello World.| +------------+
を期待するものです。
ここまでいじくってても、 open
close
print
の3メソッドがあれば成り立ちます。
実行ファイル
# coding: utf-8 from template_method.char_display import CharDisplay from template_method.string_display import StringDisplay if __name__ == '__main__': d1 = CharDisplay('H') d2 = StringDisplay('Hello World.') d3 = StringDisplay('こんにちわ。') d1.display() d2.display() d3.display()
実際どう便利か
このパターンは簡単ですが、便利な図を想像しづらいかもしれません。
例えば、このソースコードのパターンで画像で同じ出力が欲しくなった時に上記の通りに実装するといった方針が立てられます。
その上で同じスーパークラスを継承してるので、型の制限もしやすいといったメリットもあります。
紹介したソースコードは簡単なロジックでしたが、もっと複雑になってくるとロジックがテンプレートとして用意されていることは可読性に非常に効いてくるので、とても便利ですよ。
Python3.7入れる時のOpenSSLエラー
以下の記事に続き、Python3.6まで大丈夫だけど、Python3.7インストール時にopensslエラーがでました
エラーがでたら以下のような表示になります。
$ pipenv --python 3.7.2 Warning: Python 3.7.2 was not found on your system… Would you like us to install CPython 3.7.2 with pyenv? [Y/n]: Y Installing CPython 3.7.2 with pyenv (this may take a few minutes)… ✘ Failed... Something went wrong… Downloading Python-3.7.2.tar.xz... -> https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tar.xz Installing Python-3.7.2... ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib? Please consult to the Wiki page to fix the problem. https://github.com/pyenv/pyenv/wiki/Common-build-problems BUILD FAILED (CentOS Linux 7 using python-build 20180424) Inspect or clean up the working tree at /tmp/python-build.20190410073345.28295 Results logged to /tmp/python-build.20190410073345.28295.log Last 10 log lines: esac; \ ./python -E -m ensurepip \ $ensurepip --root=/ ; \ fi Looking in links: /tmp/tmpxo6kc0qg Collecting setuptools Collecting pip Installing collected packages: setuptools, pip Successfully installed pip-18.1 setuptools-40.6.2 make[1]: Leaving directory `/tmp/python-build.20190410073345.28295/Python-3.7.2'
対処法としては以下のようにopensslのアップデートをすればOKです。
$ yum update openssl
やっぱりひっかかちゃいましたね。
Pythonで学ぶデザインパターン入門 Adapterパターン
結城先生の Java言語で学ぶデザインパターン入門 のコードを Python
に書き直していくシリーズ第2段です。
Adapterパターンとは
adapterパターンとは、その名の通り電源に対する電源アダプターのポジションです。
「すでに提供されているもの」がそのまま使えない場合、それに対して上からかぶせて出力を変換するようなものですね。
ソースコード
コードの概要
ここでは、 Print
抽象クラスとして必要な機能が定義されています。それを Banner
クラスの文字を整形して出力するという機能に対して、PrintBanner
クラスでかぶせて必要な機能を作成するといったコードです。
つまり、仕様が Print
クラスで、電源が Banner
クラスで、電源アダプターが PrintBanner
クラスです。
Banner
クラスでは、
- 文字列を
"
ではさんで表示する - 文字列を
*
ではさんで表示する
の2種類の機能が用意されており、それぞれが Print
クラスに対して弱く出力する、強調して出力するに該当する出力となります。
Adapterパターンには、
- 継承をつかったパターン
- 移譲を使ったパターン
の2種類があるのでその両方のコードを紹介します。移譲は、処理を別のところに任せるという意味になります。
ソースコード
Bannerクラス(もともと用意されたもの)
# coding: utf-8 class Banner(object): def __init__(self, string: str): self.__string = string def show_with_paren(self): print('"%s"' % self.__string) def show_with_aster(self): print('*%s*' % self.__string)
前述の通り、2種類の出力機能があります。
Printクラス (必要な機能の定義)
# coding: utf-8 from abc import ABCMeta class Print(metaclass=ABCMeta): def print_week(self): pass def print_strong(self): pass
前述の通り、2種類の機能を必要としています。
javaだったら、継承の場合はinterface、移譲の場合は抽象クラスになります。
pythonはinterfaceがないので両方抽象クラスで表現しました。
PrintBanner クラス (adapter)
継承
# coding: utf-8 from adapter.inherit.banner import Banner from adapter.inherit.print import Print class PrintBanner(Banner, Print): def print_week(self): self.show_with_paren() def print_strong(self): self.show_with_aster()
継承の場合はただ継承しているだけですね。わかりやすいといえばわかりやすいです。
わかりやすい反面、制限が多いので継承はコードの自由度が低めなのでよく考えて使ったほうが良いでしょう。
移譲
# coding: utf-8 from adapter.delegation.banner import Banner from adapter.delegation.print import Print class PrintBanner(Print): def __init__(self, string: str): self.__banner = Banner(string) def print_week(self): self.__banner.show_with_paren() def print_strong(self): self.__banner.show_with_aster()
移譲は、コンストラクタでインスタンスを生成するなどしてそこに処理を移譲します。
継承する必要がないので、自由度は高いです。
実行ファイル
# coding: utf-8 from adapter.inherit.print_banner import PrintBanner if __name__ == '__main__': p = PrintBanner('Hello') p.print_week() p.print_strong()
こちらは、継承と移譲でimportが違うのですが、些細な違いなので片方だけ記載します。
実際どう便利か
めちゃめちゃ便利です。
よく考えて使わないと複雑になりがちなのですが、既存の機能に修正をいれるなどをせずに追加実装ができたりするので、とても便利です。
オープン、クローズドの原則を叶えるといった話でもとても良いものなので積極的に使ってます。
処理の共存というものは、往々にして起こりがちですがそういったものへの解決策にもなります。
ただ、かけ離れた処理を無理やりadapterパターンで期待するものへ機能を書き換えるならば、諦めて新しく処理を実装しましょう。
URLのパースを各言語で行った時の挙動の違い
URLをパースしていて不可思議な現象に立ち会ったので紹介します。
プログラムを書いていて、URLをパースしなければいけない機会というのは結構存在すると思います。
PythonとJavaScript両方で同じURLをパースしたら片方でエラーが出て、片方ではうまくいくといった現象が起こりました。
それぞれの言語ではどうなんだろう?と思って4種類程度の言語で確かめてみました。
実験方法
至ってシンプルで、
URL1 http://saruhei1989.hatenablog.com/
URL2 //saruhei1989.hatenablog.com/
の上記2リンクを用意して、それぞれを各言語でパースするだけです。
URL2はプロトコルを省略したものです。一応aタグのsrc
に指定したら両方正しく遷移するものを用意しました。
ご想像の通り、僕が遭遇した不可思議パターンのURLです。
結果
言語 | URL1 | URL2 | URLの定義 |
---|---|---|---|
Python | パースされる | パースされる | RFC3986 |
JavaScript | パースされる | エラーが出る | RFC3986, RFC3987 |
PHP | パースされる | パースされる | RFC3986 |
Ruby | パースされる | パースされる | RFC3986, RFC3987 |
Pythonの公式ドキュメント : https://docs.python.org/ja/3/library/urllib.parse.html
JavaScriptは公式が見つからなかったので仕様の記載しているもの : https://developer.mozilla.org/ja/docs/Web/API/URL/URL
PHPの公式ドキュメント : https://www.php.net/manual/ja/function.parse-url.php
Rubyの公式ドキュメント : https://docs.ruby-lang.org/ja/latest/class/URI.html
結論はJavaScriptだけエラーがでました。
言語が違うもの、違ってもおかしくないという話ですが、 RubyとJavaScriptはURLの定義どころか、実装の際に利用した仕様が全く一緒のようです。
考察
英語が難しすぎて全然理解が進まなかったのですが、 用意したURLは両方RFC3986の仕様を満たすURLではあるようです。
結論どっちが正しいとか全然わからなかったのですが、言えることは
違う言語をまたいで同じだと思われる処理をするのはやめましょう
ということですね。