Ad-social Bot

Smmok Bot

Vkserfing Bot

Vkstorm Bot

Vktarget Bot

Все программы

Запись опубликована: 08.04.2020

Swift 5.2. Обзор всех изменений

В конце марта вышел релиз Swift 5.2 для Xcode 11.4 beta. В нём улучшена диагностика ошибок, анализ зависимостей, расширен функционал SwiftPM. Обзор некоторых изменений уже был опубликован на Хабре, в этой же статье рассмотрена эволюция самого языка с возможными примерами использования.

 

SE-0249 KeyPath как функция

Шорткат кастит keyPath в функцию, где входящий параметр — сам объект, а результат — его свойство.

Давайте смотреть на примере. Создадим массив из простых моделей:

struct Model {
    let isHidden: Bool
}
let modelArray = [Model(isHidden: true), Model(isHidden: false)]

Отфильтруем массив по свойству isHidden. Ниже приведены 3 примера с одинаковым результатом:

// 1
let funcIsHidden: (Model) -> Bool = .isHidden
modelArray.filter(funcIsHidden)
// 2
modelArray.filter(.isHidden as (Model) -> Bool)
// 3
modelArray.filter(.isHidden)

Заявленная функциональность не работает в следующем примере:

//  Валидный код
let selfFunc: (Model) -> Model = .self
modelArray.compactMap(selfFunc)
// ERROR: Expression type '(Model) -> Model' is ambiguous without more context
modelArray.compactMap(.self as (Model) -> Model)
// ERROR: Key path value type 'Optional<_>' cannot be converted to contextual type '_'
modelArray.compactMap(.self)

Также, в отличие от keyPath, не работает автокомплит.

Удобно использовать для работы с массивами в таких функциях, как filter, map, reduce, sort и аналогичных.

SR-6118 Subscripts может содержать параметры по умолчанию

Всем параметрам функции можно задать значение по умолчанию. Создадим структуру Box, которая содержит массив элементов, и функцию subscript для обращения к ним.

struct Box {
    let items: [String]
    subscript(_ index: Int = 0) -> String {
        items[index]
    }
}

Теперь для обращения к первому элементу можно опустить индекс:

let box = Box(items: ["laptop, , mobile phone"])
let firstItem = box[] // "laptop"

SR-2189 Локальные функции поддерживают параметры по умолчанию из внешнего предела видимости

В повседневной практике локальные функции используются редко. Оправдать их использование бывает сложно. Тем не менее, допускаю, что в определённых случаях это может пригодиться.

Для примера создадим функцию, внутри которой опишем локальную:

func getXPosition() {
    func calculateWidth(x: CGFloat = minX) -> CGFloat { ... }
    let minX: CGFloat = 0
    ...
}

В качестве параметра по умолчанию для функции calculateWidth можем использовать значения в границах функции getXPosition.

SE-0253 Использование значений в качестве функций

Функциональность, аналогично @dynamicCallable, позволяет использовать значение в качестве функции. Но является реализацией «статического вызова».

На практике всё крайне просто: для вызова функций можно обращаться к значениям как к методам.

Создадим структуру Player:

struct Player {
    private(set) var isRunning = false
    mutating func callAsFunction() {
        isRunning.toggle()
    }
}

Создадим экземпляр Player и обратимся к нему как к функции:

var player = Player()
print(player.isRunning) // false
player()
print(player.isRunning) // true

При этом запрещено кастить, а значит, и передавать объект как функцию:

// ERROR: Cannot convert value of type 'Player' to type '() -> Void' in coercion
let playerAsFunc = player as () -> Void

Можно добавить сколько угодно методов с названием callAsFunction к классу, структуре или протоколу:

extension Player {
    func callAsFunction(isRunning: Bool = false) throws { ... }
    func callAsFunction() -> Bool { ... }
}

Применение возможно в значениях, которые представляют собой математические функции, сложные выражения или же в случаях, где значение имеет одну доминирующую функцию. Всё же не стоит злоупотреблять данным функционалом, так как он может вводить в заблуждение.

SR-4206 Исправлен баг с переопределением функции с generic-параметром

Теперь, переопределяя функцию, нельзя изменить или добавить ограничения на generic-тип. В качестве примера создадим класс CarBuilder и наследуемся от него, переопределив метод add:

protocol PowerfullEngine { }
class CarBuilder {
  func add<T>(engine: T) {}
}
class MercedesBuilder: CarBuilder {
    // Валидный код
    override func add<T>(engine: T) {}
    // ERROR: Overridden method 'add' has generic signature <T where T : PowerfullEngine> which is incompatible with base method's generic signature <T>; expected generic signature to be <T>
    override func add<T: PowerfullEngine>(engine: T) {}
}

SR-11298 Расширение протокола без ограничений на класс наследует это ограничение

В случае наложения ограничения на Self, расширение применит ограничение.

К примеру, создадим протокол Menu и extension с ограничением по классу:

protocol Menu {}
class SideMenu: Menu {
  var sectionHeight = 0
}
extension Menu where Self: SideMenu {
  var menuHeight: Int {
    get { sectionHeight * 20 }
    set { sectionHeight = newValue / 20 }
  }
}

При этом сеттер — nonmutating, как будто протокол имеет ограничение на класс.

SR-11429 Кастинг для функций с лейблами

При кастинге функции к типу лейблы теперь убираются. Благодаря этому можно кастить функции с лейблами. Прежде данная функциональность работала только в функциях без лейблов:

func setX(x: Int) {}
(setX as (Int) -> Void)(5)

При этом значения по умолчанию сохранить не выйдет:

func setPoint(x: Int, y: Int = 0) {}
(setPoint as (Int, Int) -> Void)(5, 1)

Это применимо и к generic-ам, поэтому данный код больше не валиден.

typealias Point<T> = T
func set(x: Int) {}
// Валидный код
(set as Point)(5)
// ERROR: Extraneous argument label 'x:' in call
(set as Point)(x: 5)

SR-11841 Функция filter(_:) вызывается в ожидаемом порядке в lazy-коллекциях

Создадим lazy-коллекцию и вызовем метод count:

let lazyArray = [0]
    .lazy
    .filter { _ in print("A"); return true }
    .filter { _ in print("B"); return true }
// Результат A B
_ = lazyArray.count

Прежде результат вызова был обратный: B A.

SR-2790 Ряд инициализаторов типов семейства UnsafePointer / UnsafeBufferPointer теперь выдаёт предупреждение

Ограничение действует на строки, массивы и inout-параметры, которые не существуют за пределами вызова функции.

Создадим структуру, принимающую UnsafePointer в качестве параметра при инициализации:

struct Game {
    let info: UnsafePointer<Int8>
}
func createGame() {
    var i: Int8 = 0
    // WARNING: Initialization of 'UnsafePointer<Int8>' results in a dangling pointer
    _ = Game(info: .init(&i))
    // WARNING: Passing '[Int8]' to parameter, but argument 'character' should be a pointer that outlives the call to 'init(character:)'
    _ = Game(info: [1, 2])
    // WARNING: Passing 'String' to parameter, but argument 'character' should be a pointer that outlives the call to 'init(character:)
    _ = Game(info: "")
}

Всего изменений в этой версии было 9, тем не менее, они определённо внесли новую функциональность. Предполагаю, что самым используемым из текущих будет KeyPath как функция.

А мы с нетерпением ждём следующую версию. Судя по всему, в ней будет поддержка Swift на Windows и появятся интересные фичи, такие как использование self в escaping-замыканиях без обращения через self.* (SE-0269) и расширится функциональность generic-ов (SE-0267).
Источник

Source: habr1

Метки:





Запись опубликована: 08.04.2020

[Перевод] Переживёт ли Airbnb коронавирус? [спойлер: да]

Рынок краткосрочной аренды штормит в результате краха туристической отрасли, вызванного коронавирусом. Вернёт ли главный игрок отрасли свои позиции после того как всё закончится?

Что будет с услугой home-sharing после коронавируса? Один из животрепещущих вопросов о судьбах допандемических гигантов, наподобие Airbnb. Эта компания и её конкуренты перекроили рынок туристических услуг, изменив при этом районы и целые города, когда волна перехода на краткосрочную аренду прокатилась по туристическим районам мира. Сейчас же, с замороженным туризмом, впавшими в кому национальными экономиками и нежеланием людей теперь делить общее пространстве с незнакомцами — будущее подобных компаний под очень большим вопросом, Перспективы отрасли самые мрачные.

Статья написана при поддержке компании EDISON.
Мнение Заказчика: 10 плюсов программистов из EDISON
Это интересно и полезно знать: Завтрак программиста

Ближайшее будущее не вызывает ни малейшего оптимизма. Заказы Airbnb по всему миру резко просели. Аналитики данных AirDNA утверждают, что количество заказов по всей Европе упало в разы. Сначала произошло пятикратное обрушение, когда на третьей неделе марта произошло падение на 80% по сравнению со второй неделей. После этого уже мало чему осталось падать, но в последнюю неделю марта показатели рухнули ещё на 10% по сравнению с третьей неделей. В США, где реакция на свирепствующий вирус отставала от европейской, цифры падений в бронировании неравномерны, но едва ли менее драматичны. К середине марта заказы в Нью-Йорке, Сан-Франциско и Сиэтле уже рухнули более чем наполовину по сравнению с началом января, в Вашингтоне и Чикаго показатели просели на треть.

Чтобы пережить кризис, Airbnb, по слухам, отменил всю маркетинговую деятельность, приостановил выплаты основателям и вдвое сократил зарплату топ-менеджеров. Прекращено всё, кроме необходимого найма, отменены все публичные мероприятия. Массовых увольнений пока не было, но они, похоже, на повестке дня. «Airbnb устойчив и выдержит сложные времена, мы делаем всё возможное, чтобы укрепить наше сообщество и нашу компанию», — говорится в недавнем заявлении Reuters.

В быстроразвивающейся ситуации компания предложила полную отмену любых предварительных бронирований — что вошло вразрез с пожеланиями хозяев, полагающих, что правила отмены, которые они уже согласовали с гостями, сохранятся. Понимая их недовольство, основатель компании Брайан Чески обрисовал дилемму компании: «Если бы отмена бронирования предполагала выплату возмещения, это могло бы иметь серьезные последствия для вас. Абсолютно недопустимо ставить под угрозу жизнь и здоровье гостей и хозяев, которые могут оказаться в небезопасных ситуациях».

Чтобы восстановить натянутые отношения с владельцами, Airbnb создал фонд в 250 миллионов долларов, для компенсации хостам хотя бы четверти потерянного дохода. Также предусмотрен дополнительный фонд в 10 миллионов долларов для супер-хостов. Хозяева из США имеют право подать заявку на освобождение от выплат по причине Covid-19. Airbnb также обратилось с просьбой к правительству Канады распространить подобные льготы на хосты в стране.

Эти меры поспособствуют возвращению доброжелательного отношения хозяев, что будет очень важно после восстановления туризма. Но когда произойдёт возрождение, в какой форме, и как это будет выглядеть в городах, где у Airbnb были самые устойчивые позиции — пока не знает никто.

Мобилизация в кризис

На данный момент многие из тех, чьё жильё сейчас не арендовано, находят ему иное достойное применение. Хозяева, зарабатывающие до того краткосрочной арендой, по всему миру предложили кров более чем 100 000 нуждающимся людям. Это и жильё для медперсонала в Италии, которым необходимо жить поблизости от своих больниц. И крыша над головой для тех, кто решил самоизолироваться во время кризиса. И даже временный приют для бездомных. В Барселоне, городские власти арендовали 200 квартир на короткий срок, чтобы позволить самоизолироваться тем людям, которые в противном случае оказались бы на улице.

Когда самый тяжёлый кризисный период будет позади, возможны и другие пути развития сложившейся ситуации.

Возрождение начнётся с деревни

Если по мере стабилизации кризиса ограничения будут сняты, а некоторые маршруты возобновят работу, городские варианты могут оказаться не первыми, которые будут востребованы.

«Я думаю, что в более изолированных сельских районах положение Airbnb, скорее всего, будет достаточно устойчивым», — говорит Мари Хикки, руководитель отдела коммерческих исследований в сфере недвижимости в консалтинговой компании из Великобритании. «Вполне возможно, что мы не увидим действительно устойчивого восстановления потока иностранных посетителей до 2021 года. Рынок, который быстрее всего восстановится, может быть внутренним рынком отдыха».

В то время как люди могут быть более осторожны в поездках в другие страны, городские жители, оказавшиеся взаперти в своих городских квартирах, вполне могут воспользоваться возможностью поехать куда-нибудь в окрестности подышать свежим воздухом, как только это станет безопасным.

Отели отобьют позиции

Когда путешественники вернутся в города, не факт что Airbnb первым извлечёт из этого выгоду. Некоторые эксперты полагают, что, когда туристическая индустрия начнет возрождаться, может произойти среднесрочный возврат к традиционным отелям ввиду опасений относительно того, насколько будут соблюдаться гигиенические стандарты на сфере домашних вариантов. «Люди могут быть менее склонны бронировать Airbnb после отмены карантина из-за проблем с чистотой», — говорит Майкл О’Риган, старший преподаватель по маркетингу в Университете Великобритании в Борнмуте. «Хосты просто не смогут гарантировать основательную дезинфекцию после каждого гостя».

Впрочем, пока будут свежи в памяти недавние запреты на общение, путешественники могут опасаться делить общее пространство в отелях с большим количеством посетителей. Хикки предсказывает возможный поворот в сторону ранее нишевого сектора: многоквартирных домов, которыми управляют отели. «Мы могли бы увидеть обслуживаемые квартиры или так называемые апарт-отели главными бенефициарами ситуации», — говорит она. «Они похожи на то что предлагает Airbnb. И там пользователи уверены в том, что санитарные нормы будут как в отеле, с регулярной уборкой, а также с соблюдением правил техники безопасности».

Этот сектор уже вырос за последние годы, частично благодаря концепции пребывания в квартирах во время путешествий, которая так широко освещается бумом Airbnb. Теперь он может оказаться самым быстрым сектором для восстановления. Это не было бы плохо для городов, нуждающихся в наличности — жилье гостиничного типа, как правило, приносит большие налоги и подразумевает рабочие места для обслуживающего персонала.

Объекты Airbnb вернутся на рынок долгосрочной аренды

Спад заставит многих владельцев Airbnb найти альтернативные способы погашения своих кредитов — возможно, путем поиска долгосрочных арендаторов.

В интернете много дискуссий (и тон этих обсуждений одобрительный), о том, что бывшие Airbnb-квартиры возвращаются на рынок долгосрочной аренды. Например, в Дублине число квартир с одной и двумя спальнями, которые можно арендовать в центре Дублина, в марте достигло пятилетнего максимума, при этом зачастую на фотографиях эти квартиры имеют признаки того, что раннее они использовались для размещения туристов.


Погляжу, что с хостингом на Airbnb теперь полная ж**а. Все эти квартиры, совершенно не связанные друг с другом, внезапно одномоментно появились в аренде. И все как на подбор с маленькими полотенцами, сложенными на кроватях! Какое внезапное гостеприимство на нынешнем рынке аренды в Дублине.

Похожая тенденция наблюдается в Лондоне и Мадриде. В Амстердаме многие арендаторы настроены оптимистично, они ищут долгосрочных арендаторов — но только до лета.



Владельцы недвижимости Амстердама ожидают возвращения массового туризма через 3–6 месяцев. Если взглянуть на бывшие квартиры Airbnb, теперь временно сдаваемые в помесячную аренду в Pararius, то можно заметить — все оговаривают конкретную дату окончания срока аренды.

Многие только рады, что хотя бы часть предложений переместилась в аренду, необходимость подобной коррекции уже назрела давно. Качество жизни местных может улучшиться в некоторых районах, где раньше был интенсивный туристический поток. В частности, в Старом городе (Барселона), если бо́льшую часть квартир теперь будут занимать долгосрочные арендаторы, отсутствующие полный рабочий день, шума в районе будет поменьше, при этом возникнет более широкая экосистема из местных магазинов и сервиса.

Долгое возвращение на круги своя

Но возможен и другой итог — нынешний глобальный шок в жизни миллионов людей в конечном итоге мало что поменяет. «Во время эпидемий атипичной пневмонии и MERS было много разговоров о том, как это изменит людей», — говорит О’Риган, — «но в итоге дела пошли, как обычно, причём довольно быстро. Я не думаю, что и Covid-19 — смертельный удар. Многие люди довольно скоро вернутся к хостингу своего жилья».

Ни одна из этих предыдущих глобальных вспышек не была со столь же далеко идущими последствиями, как пандемия Covid-19, конечно, — нынешний кризис еще толком и не разразился — но, когда количество новых инфицированных пойдёт на спад, возникнет сильное давление со стороны городов и людей, жаждущих вернуться к нормальной жизни и бизнесу как можно скорее. В частности, в Амстердаме пандемия обходится городу в 1,6 миллиарда евро в месяц. Учитывая, что существенная доля этой суммы — потери отелей, ресторанов и прочих кафешек, многие не будут беспокоиться о чрезмерном туризме в течение длительного времени.

Кризис также обнажил ещё один недостаток Airbnb, вызывающий острую критику — обвинение в том, что сервис усугубляет нехватку городского жилья, убирая квартиры с рынка долгосрочной аренды. В городах, в которых краткосрочная аренда сильнейшим образом повлияла на цены и качество жизни, таких как Париж и Барселона, вполне возможно, что перерыв в туризме может расширить рынок аренды и упростить доступность для местных жителей.

Source: habr1

Метки:





Запись опубликована: 08.04.2020

Архитектура для начинающих или почему не нужно вставлять флажок в человека-меча

Аннотация:

  1. Пример реализации нового функционала в классе через добавление «флажка».
  2. Последствия.
  3. Альтернативный подход и сравнение результатов.
  4. Как избежать ситуации: «Архитектурный оверкилл»?
  5. Момент, когда приходит время всё менять.

Перед тем как начать, пара замечаний:

  • Это история об архитектуре ПО — в значении, которое использует дядя Боб. Да, тот самый.
  • Все персонажи, их имена и код в статье — вымышленные, любые совпадения с реальностью случайны.

Допустим, я — рядовой программист-программист на проекте. Проект представляет собой игру, в которой единственный Герой (aka Hero) идёт по идеально горизонтальной прямой слева направо. Этому замечательному путешествию мешают монстры. По команде пользователя Герой лихо рубит их мечом в капусту и в ус не дует. В проекте уже 100К строк, и «нужно больше строк фич!» Посмотрим же на нашего Героя:

class Hero {
    func strike() {
        // некий код 1
    }
    // ещё больше кода
}

Предположим также, что мой личный тимлид Василий тоже может в отпуск. Внезапно. Кто бы мог подумать? Далее ситуация развивается стандартно: прилетает Сова и требует срочно, вот прям вчера, сделать возможность выбирать перед началом игры: играть за Героя с дубиной или с мечом.

Надо очень быстро. И да, естественно, он сам был программистом больше лет, чем я умею ходить, поэтому он знает, что задача — на пять минут. Но так и быть, оценим на 2 часа. Надо только добавить флажок и if-чик.

Замечание: if-чик в значении @!^%$#@^%&@!11$ его #$%@^%&!@!!! в &!^%#$^%!1 if-чик!

Мои мысли: “Ха! Два часа! Готово!”:

class Hero {
    enum WeaponTypes {
        case sword:
        case club:
    }
    var weaponType: WeaponTypes?
    func strike() {
        guard let weaponType = weaponType else {
            assertionFailure()
            return
        }
        // Я крут: switch в Swift лучше if-ов - предупреждает о необработанных кейсах!
        switch (weaponType) {
            case .sword: // некий код обработки удара мечом
            case .club:  // некий код обработки удара дубиной
        }
    }
    // больше кода
}

Если Вы узнали в моём решении своё, то, увы: у меня для Вас две новости:

  1. Как бы хорошая: мы оба доставляем. Доставляем — от слова deliver value или от слова смешной (сквозь слёзы) код.
  2. И плохая: без Василия проекту капец.

Итак, что же случилось? Казалось бы, пока ничего. Но давайте посмотрим, что случится дальше (пока мы всеми силами удерживаем Василия в отпуске). А дальше будет вот что: отдел QA обратит внимание на вес нашего Героя. И нет, это не потому, что Герою пора присесть на диету, а вот почему:

var weight: Stone {
    return meatbagWeight + pantsWeight + helmetWeight + swordWeight
}

Забыл исправить расчёт веса. Ну подумаешь, ошибочка, с кем не бывает?! Тяп-ляп, трах-тибидох, готово:

var weight: Stone {
    // пропустим код guard let weaponType
    let weightWithoutWeapon = meatbagWeight + pantsWeight + helmetWeight
    switch (weaponType) {
        case .sword: return weightWithoutWeapon + swordWeight
        case .club:  return weightWithoutWeapon + clubWeight
    }
}

Но быстро же исправил! Герой-то теперь прыгает с учетом веса зато. Работа ориентированная на результат! Это вам не пупочки разглядывать, астронавты архитектурные.

Ну правда, потом ещё немного поправил. В списке заклинаний, пришлось сделать так:

var spells: [Spells] {
    // пропустим код guard let weaponType и
    // код, чтобы подготовить let spellsWithoutWeapon: [Spells]
    switch (weaponType) {
        case .sword:
            // подготовка let swordSpells: [Spells]
            return spellsWithoutWeapon + swordSpells
	case .club:
            // подготовка let clubSpells: [Spells]
            return spellsWithoutWeapon + clubSpells
    }
}

А потом пришёл Петя и всё сломал. Ну правда, у нас есть такой джун на проекте. Невнимательный.

Ему всего-то надо было добавить понятие “Уровень оружия” в формулы расчёта силы удара и веса Героя. А Петя пропустил один из четырех случаев. Но ничего! Я всё поправил:

func strike() {
    //guard let weaponType
    switch (weaponType) {
        case .sword: // некий код обработки удара мечом с учетом weaponGrade
        case .club:  // некий код обработки удара дубиной с учетом weaponGrade
    }
}
var weight: Stone {
    // guard let weaponType
    let weightWithoutWeapon = meatbagWeight + pantsWeight + helmetWeight
    switch (weaponType) {
        case .sword: return weightWithoutWeapon + swordWeight / grade
	case .club:  return weightWithoutWeapon + pow(clubWeight, 1 / grade)
    }
}
var spells: [Spells] {
    // А тут ничего не поменялось, ура!
    // Правда, разросшийся метод увеличил объем класса. Искать внутри Героя код, имеющий отношение к задаче, стало чуть сложнее. Но это ничего!
}

Стоит ли говорить, что когда (внезапно!) понадобилось добавить лук / обновить формулы, опять были забытые кейсы, баги и вот это вот всё.

Что же пошло не так? Где я был неправ, и что (кроме матов) сказал Василий, когда вернулся из отпуска?

Тут можно не читать историю дальше, а, например, подумать о вечном, об архитектуре.

А с теми, кто всё же решил читать, продолжим.

Итак, обратимся к классикам:

Преждевременная оптимизация — корень всех зол!

А… э… это не то. Вот то:

В ООП-языках (например в Swift) есть три основных способа расширить возможности класса:

  1. Первый способ — “наивный”. Мы видели его только что. Добавление флажка. Добавление ответственности. Разбухание класса.
  2. Второй способ — наследование. Всем известный мощный механизм переиспользования кода. Можно было бы, например:
    • Отнаследовать новых ГерояСЛуком и ГерояСДубиной от Героя (который с мечом, но это сейчас не отражено в названии класса Hero). И затем в наследниках переопределить изменившиеся методы. Этот путь очень плох (просто поверьте мне).
    • Сделать базовый класс (или протокол) Герой, а все особенности связанные с конкретным типом оружия убрать в наследников:
      ГеройСМечом: Герой,
      ГеройСЛуком: Герой,
      ГеройСДубиной: Герой.
      Это лучше, но ведь сами имена этих классов смотрят на нас как-то недовольно, свирепо и в то же время грустно и с недоумением. Если на кого-то они так не смотрят, то постараюсь написать ещё статью, где помимо скучного маскулинного Героя будут они…
  3. Третий способ — сепарирование ответственности через инъекцию зависимости. Это может быть зависимость закрытая протоколом или замыкание (как бы закрытое сигнатурой), что угодно. Главное, чтобы реализации новых ответственностей ушли из основного класса.

Как это может выглядеть в нашем случае? Например, так (решение от Василия):

class Hero {
    let weapon: Weapon // зависимость класса Герой, т.е. Герой зависит от оружия
    init (_ weapon: Weapon) { // точка инъекции или внедрения зависимости
        self.weapon = weapon
    }
    func strike() {
        weapon.strike()
    }
    var weight: Stone {
        return meatbagWeight + pantsWeight + helmetWeight + weapon.weight
    }
    var spells: [Spells] {
        // подготовка как раньше
        return spellsWithoutWeapon + weapon.spells
    }
}

Что нужно, чтобы так было? Ниндзютсу — protocol:

protocol Weapon {
    func strike()
    var weight: Stone {get}
    var spells: [Spells] {get}
}

Пример реализации протокола:

class Sword: Weapon {
    func strike() {
        // то, что раньше валялось в Hero в switch внутри кейса .sword
    }
    var weight: Stone {
        // то, что раньше валялось в Hero в switch внутри кейса .sword
    }
    var spells: [Spells] {
        // то, что раньше валялось в Hero в switch внутри кейса .sword
    }
}

Аналогично Sword-у пишутся классы для: Club, Bow, Pike, etc. «Легко видеть» (с), что в новой архитектуре весь код, который относится к каждому конкретному типу оружия, сгруппирован в соответствующем классе, а не размазан по Герою вместе с остальными типами оружия. Это облегчает чтение и понимания Героя и любого конкретного оружия. Плюс, благодаря требованиям накладываемым протоколом гораздо проще отследить все методы, которые нужно реализовать при добавлении нового типа оружия или при добавлении новой фичи к оружию (например, у оружия может появиться метод расчёта цены).

Тут можно заметить, что инъекция зависимости усложнила создание объектов класса Hero. То, что раньше делалось как просто:

let lastHero = Hero()

теперь превратилось в набор инструкций, который будет дублироваться везде, где необходимо создать Героя. Впрочем, Василий позаботился и об этом:

class HeroFactory {
    static func makeSwordsman() -> Hero { // хотя, насчет static - это неточно
        let weapon = Sword(/* аргументы */)
        return Hero(weapon)
    }
    static func makeClubman() -> Hero {
        let weapon = Club(/* аргументы */)
        return Hero(weapon)
    }
}

Понятно, что Василию пришлось попотеть, чтобы раскидать кучу, которую навалили спроектировали Петя (и я).

Конечно, глядя на последнее решение, может возникнуть следующая мысль:

Ок, получилось, норм. Удобно читать и расширять, но ведь все эти фабрики / протоколы / зависимости — это куча оверхеда? Код, который ничего не даёт с точки зрения фич, а существует только для организации кода. «Код для организации кода», мгм. Неужели нужно городить этот огород всегда и везде?

Честный ответ на первый вопрос будет таким:

Да, это оверхэд к фичам, которые так любит бизнес.

А на вопрос “когда?” отвечает раздел:

Философия человека-меча или “когда же надо было править?”

Вначале был человек-меч. В системе смыслов старого кода это было вполне нормально. Пока был один Герой и одно оружие, не было необходимости и различать их — всё равно ничего другого для героя не было. И монолитный код своим текстом утверждал этот факт.

Человек-меч — это даже звучит не так плохо.

А к чему привели первые «наивные» правки? К чему привело добавление if-чика?

Добавление if-чика привело к возникновению… мутанта! Мутанта, т.е. мутабельного объекта, который может мутировать между «человекомеч» и «человекодубина». При этом, если немного ошибиться в реализации мутанта, то возникает состояние «человекомечедубина». Не надо так! Не надо «человекодубины» вообще.

Надо:

  1. Человек + зависимость от меча (потребность в мече);
  2. Человек + зависимость от дубины (потребность в дубине).

Не всякая зависимость — зло! Это зависимость от алкоголя — зло, а от протокола — добро. Да даже зависимость от объекта лучше, чем “человекодубина”!

Когда произошло превращение в мутанта? Превращение в мутанта произошло в момент добавления флажка: монолитный код так и остался монолитным, но при изменении (мутации) флажка поведение одного и того же объекта стало существенно меняться.

Василий выделил бы здесь две стадии мутации:

  1. Добавление флага и самого первого «if» (или «switch», или другого механизма ветвления) по флагу. Ситуация угрожающая, но терпимая: Героя поливают радиоактивными отходами, но он превозмогает.
  2. Появление в классе более одного «if» по данному флагу, особенно в разных методах класса. Всё. Героя уже нет — перед нами мутант. Мутант, у которого постоянно что-то отваливается из-за багов.

Таким образом, в рассмотренной ситуации для того, чтобы не допускать возникновения технического долга, имеет смысл выстраивать архитектуру до прохождения первой стадии, в самом худшем случае — до второй.

Что именно сделал Василий, чтобы полечить мутанта?

С технической точки зрения — применил инъекцию зависимости закрытой протоколом.

С философской — разделил ответственность.

Все особенности работы класса, которые могут взаимозаменяться друг с другом (а значит — альтернативны между собой), были вынесены из Героя в зависимости. Новый, альтернативный функционал — реализация работы меча, реализация работы дубины — по факту своего появления стал различен между собой и отличен от остального по прежнему безальтернативного кода Героя. Так уже в «наивном» коде появилось нечто новое, отличное по своему альтернативному поведению от безальтернативной части Героя. Так в «наивном» коде возникло неявное, размазанное по Герою описание новых бизнес-сущностей: меча и дубины. Для того, чтобы было удобно оперировать новыми бизнес-сущностями, стало необходимо выделить их как отдельные сущности кода, обладающие собственными именами. Так произошло разделение ответственности.

P.S. TL;DR;

  1. Видишь флажок?
  2. Будь мужиком, блин! Сотри его!
  3. Инъекция зависимости
  4. Profit !11

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

P.P.S. Серьезный опрос!

  • 6,1%Тема слишком простая, уже 100500 раз описано, такое никому не будет интересно3
  • 26,5%Я начинающий разработчик, мне понравилось13
  • 57,1%Я опытный разработчик, считаю, что для начинающих годно28
  • 2,0%Тема слишком сложная, в реальности такое никому не нужно1
  • 8,2%Изложение столь плохо, что бессмысленно оценивать тему4
Проголосовали 49 пользователей. Воздержались 4 пользователя.

Теги:
Хабы:


Source: habr1

Метки:





Запись опубликована: 07.04.2020

[Перевод] Конференция DEFCON 26. Виляние хвостом: скрытое пассивное наблюдение. Часть 1

  • Перевод
В наш цифровой век технически грамотных противников мы забываем о том, что существует необходимость использования физического наблюдения за целью методами «старой школы». Многие организации используют группы наблюдения: внутренние для правительственных учреждений или внешние, нанятые для выполнения конкретной задачи. Цели этих групп варьируются от подозреваемых в терроризме до людей, обвиняемых в фиктивных страховых исках.

В то время как большинство людей думают, что они никогда не окажутся под слежкой, некоторые профессии увеличивают эту вероятность. Например, если вы являетесь журналистом, который встречается со своими источниками информации только лицом к лицу, то можете стать мишенью для наблюдения, особенно если источник является осведомителем или имеет информацию, которую его работодатель предпочел бы не давать. Также не стоит считать невероятной возможность слежки за хакером, пентестером, спикером или участником DEFCON.

Эти команды наблюдателей — не одинокие частные детективы, сидящие в своей машине в конце улицы, на которой вы живете, а высококвалифицированные люди, чья работа заключается в том, чтобы оставаться незамеченными. Они наблюдают, идентифицируют ваши контакты и документируют все, что видят или слышат. Они стремятся выглядеть людьми, которых вы не сможете описать, если вас об этом попросят. Их методы слежки очень мало изменились за десятилетия, потому что эти методы реально работают.

Этот доклад посвящен методам мобильного и пешего наблюдения, которые используют такие группы. Спикеры посоветуют, как определить, не наблюдают ли за вами, и каким образом можно усложнить жизнь этим наблюдателям.

Agent X: давайте сразу перейдем к сути дела. Почему мы решили посвятить этот разговор наблюдению? Мы будем обсуждать вопросы слежки за людьми образца, скажем 1983 года, а не рассказывать о современных технических методах наблюдения.

SecuritySense: … типа Facebook.

Agent X: возможно. Итак, не являетесь ли вы целью наблюдения?

SecuritySense: вполне возможно, что в этом зале присутствуют люди, которые представляют интерес для слежки. Крайний справа на слайде – Зак Франклин, который вел DefCon в течение 15 лет, но и сам DefCon может быть нацелен на вас. На этом слайде вы видите Мишель Мэдиган. Некоторые из вас помнят ее нападки на нашу конференцию, так что мы решили нанести ответный удар собственной командой наблюдения, работавшей под прикрытием. Настоящее физическое наблюдение – это отличное времяпровождение.

Agent X: … особенно когда вы пребываете во враждебном окружении. Если ваш домашний офис выглядит примерно так, как на этом слайде, значит, вы никогда не выходите из своего дома и не можете быть целью для наблюдения.

SecuritySense: похоже, что у него в офисе серьезные проблемы для тех, кто захочет его прибрать.

Agent X: итак, кто обычно ведет наблюдение? Это известные агентства из трех букв: NSA (Агентство национальной безопасности), FBI (Федеральное бюро расследований), CIA (Центральное разведывательное управление), KFC. Хотя, пожалуй, KFC здесь не причем, если вы не пытаетесь продать их секретный рецепт Северной Корее. Многие компании имеют возможность задействовать группы наблюдения, если испытывают в них потребность. Множество правительственных структур, особенно в Великобритании, такие как Налоговая служба и таможня ее Величества, разыскивают мошенников с помощью специальных групп наблюдения. Как частное лицо, вы можете пройти 2-х недельное обучение для работы в такой группе. Если вы бывший военный наблюдатель, достаточно предъявить 2 документа, удостоверяющих личность, заплатить 2314 фунтов, пройти курс и получить соответствующий сертификат на право заниматься оперативной слежкой.

SecuritySense: это примерно 2000 долларов.

Agent X: да, достаточно дорого. Итак, с самого начала доклада следует отметить важный факт. Весь транспорт, с которого ведется наблюдение, характеризуется так называемым «состоянием тепла». Предположим, вы начали наблюдение за целью в 10 утра и ведете его целый день. Каждый раз, как вы контактируете с целью или привлекаете внимание цели, это состояние падает до тех пор, пока вы окончательно не спалитесь, как этот тост. Например, врезавшись своей машиной в автомобиль цели наблюдения.

Поэтому в конце рабочего дня все операторы наблюдения сообщают, какой степени достигло их «состояние тепла». Как человек, находящийся под наблюдением, вы должны стремиться к тому, чтобы оно опустилось до нуля, то есть засечь наблюдение за собой и провалить незаметную слежку.

Само наблюдение состоит из 3-х основных этапов: Pickup, или засечка цели, Follow – преследование цели и Housing – наблюдение за домом цели. Больше всего разговоров у нас будет о преследовании, так как это самая веселая часть наблюдения, во время которой происходят основные события. О засечке, или захвате цели для наблюдения, мы поговорим кратко, потому что ее смысл – получить контроль над перемещениями цели в самом начале операции или операционного дня. Для этого наблюдатель должен иметь описание цели, координаты ее начального местонахождения и возможные пути перемещения. Для захвата цели в первую очередь используется стационарный пост наблюдения, например, соседняя квартира или дом напротив.

SecuritySense: на следующем слайде белое здание слева – это российское посольство в Вашингтоне, а домик напротив него, показанный справа – это стационарный пост наблюдения ФБР. Они владели этой недвижимостью и вели наблюдение за зданием через дорогу на протяжении многих лет.

Вы можете спросить, где же огромные камеры наблюдения, так вот, никто из целей не проходит через главный вход, они всегда выходят через заднюю дверь на соседнюю улицу.

Agent X: наблюдатели также используют стационарные скрытые камеры с дистанционным управлением, которые автоматически включаются при появлении или удалении объекта наблюдения. Они также широко используют мобильные пункты наблюдения. На следующем слайде вы видите передвижной пункт наблюдения образца 1989 года – это микроавтобус «Додж», который использовали ФБР. Один парень купил этот автомобиль на аукционе и затем выложил видео на YouTube. На крыше машины расположена антенна для прослушивания дома объекта наблюдения, каждая из передних и задних фар снабжена микрофоном, вы видите оставшиеся от них отверстия, а рейлинги на крыше снабжены видеокамерами. Внутри расположены мониторы видеонаблюдения за объектом, а также туалет и кондиционер.

SecuritySense: конечно, он не такой симпатичный, как микроавтобус на следующем слайде, но если вы увидите такой на своей улице, знайте, что вы облажались.

Agent X: наблюдатели также могут использовать специальное транспортное средство для визуальной слежки, которое у нас в Англии называется boot fit, а в Америке — trunk fit. В такой машине оператор наблюдения находится в багажнике. Это не очень удобно, но зато наблюдатель может вести видеосъемку, делать фотографии и просто наблюдать за объектом из автомобиля, который выглядит совершенно пустым. Это особенно удобно там, где припаркованный здоровенный фургон сразу же привлечет внимание. На слайде вы видите такой автомобиль без водителя и пассажиров в салоне, но наблюдателя выдает отблеск объектива камеры, расположенной за задним стеклом. Такую слежку можно проводить очень ограниченное время, потому что оперативник в багажнике рискует здоровьем, но это работает.

Эти методы существуют годы и мало изменились. Есть кинофильм, снятый британцами в 1974 году, которым они поделились с нашими американскими друзьями в рамках «особых отношений».
На экране демонстрируется отрывок фильма. Диктор: машина сопровождения остается на месте, и как только автомобиль цели проходит мимо нее, наблюдатель передает информацию другой оперативной машине, которая должна следовать за объектом наблюдения. Машина Брауна движется на запад, и если он посмотрит в зеркало заднего вида, то не увидит ничего подозрительного. Однако примерно в километре от дома из переулка на дорогу выезжает зеленый «Магнум», который следует за Брауном. Это выглядит как обычный дорожный трафик, и хотя цель не увидела никаких «висящих на хвосте» машин, большую часть времени Браун находился под наблюдением.

Ключевой фразой является «находился под наблюдением». Машины наблюдателей не сопровождают цель постоянно, не едут прямо за ней, потому что лучше потерять объект, чем провалить операцию из-за того, что объект заметит за собой слежку. Вы слышали, как оперативники открыто переговариваются по радио. Этот способ передачи сообщений давно устарел, и в наше время для экономии времени и большей оперативности используются краткие кодовые фразы. Это также позволяет скрыть местоположение и направление движения цели, потому что если наблюдатель не использует шифрование, его сообщение можно перехватить, и посторонний человек или сам объект окажется в курсе происходящего. Вот так звучит сообщение в старой классической манере: «Ожидаем. Цель вышла из своего дома в красной бейсболке, синей майке, серых брюках и черных ботинках. Объект садится в свою машину. Теперь он отъезжает и поворачивает налево к перекрестку Хай Стрит и Уотер Лейн», а так – его современный эквивалент: «Семьдесят семь. Альфе 1 от Чарли 1. Синий на сером. Черные туфли, красная бейсболка. Выполнил Браво 1. Мобильный 91 желтый 2». Звучит как чушь собачья, но команда наблюдателей понимает, что последняя часть сообщения использует так называемый spot code – обозначения, которые наносятся на карты местности и позволяют указать местоположение цели и направление движения вместо того, чтобы произносить названия улиц: «Машина цели приближается к перекрестку В4668 и А47. Включил правый поворотник. Сейчас цель повернула направо, направляясь к Earl Shilton».

Посмотрев на спот-карту, можно просто сказать: «Браво 1 на красной шестерке следует направо. Браво 1 совершил «направо», 91 Красная пятерка», и все оперативники понимают, где находится цель и куда она направляются.

SecuritySense: такие карты составлены для всей зоны операций, для всего города, на каждый автомобиль наблюдения, сколько бы их не было.

Agent X: обычно используются 8 машин и 16 операторов.

SecuritySense: да, это много папок с информацией, которыми нужно быстро снабдить большую команду наблюдателей, так что это совсем не дешевая работа, если ее делать как следует.

Agent X: итак, что мы должны искать позади, чтобы понять, что за нами ведется наблюдение? Если вы в Техасе, где все ездят на джипах, наблюдатели тоже будут на джипе. Если вы в городе, они будут в «седане», так чтобы слиться с окружением и не выделяться. Они не будут следить за вами на здоровенном SUV с правительственными номерами.

SecuritySense: кроме округа Колумбия!

Agent X: да, кроме DC. Вот выдержка из местной газеты штата Айова. Здесь сообщается, что только в одном штате правительственным агентствам, занимающимся слежкой под прикрытием, было выдано более 200 автомобильных номеров штата Айова. Поскольку это команда профессионалов, им нужно иметь несколько номерных знаков для замены. Если «состояние нагрева» падает, они считают, что нужно поменять номер автомобиля. Однако вы не можете встать на обочине шоссе и начать откручивать номер, потому что кто-то это заметит и сообщит в полицию. Кроме того, им придется отложить преследование на некоторое время.

Как я сказал, они не будут ехать прямо за вами, а станут использовать 2-3 подменные машины и параллельные маршруты, чтобы опередить вас. В Америке это очень легко – большинство ваших городов разбиты на блоки, поэтому достаточно расставить синие машины наблюдения на параллельных улицах так, чтобы перехватывать наблюдение за красной машиной при ее поворотах, заранее сообщая о них оперативникам. На слайде изображена стандартная «плавающая коробка» ФБР. Анимация показывает, что при повороте цели все машины оперативников сдвигаются так, чтобы держать наблюдаемого внутри коробки.

Сигналы светофора не могут помешать преследованию, так как если одна машина наблюдения останавливается на «красный», вторая выезжает с параллельного маршрута и продолжает следовать за целью.

Как можно противостоять такой слежке? Первое, что вы сделаете как человек, пытающийся засечь за собой слежку – это использовать анти-слежку, то есть нанять собственного наблюдателя. Этот парень сопровождает вас, чтобы обнаружить тех, кто за вами следит. Если вы не можете позволить подобного, самое простое – это постоянно изменять свой маршрут и пользоваться общественным транспортом. Вы можете выехать из дома на своем авто, припарковать его где-нибудь и сесть на автобус.

Хорошим решением будет вызвать такси, потому что такси и автобусы обычно следуют по таким маршрутам, которые не используются личным транспортом. Если ваш автобус движется по «выделенке», то ни один наблюдатель не посмеет его сопровождать. Им придется задуматься, зачем вы поехали в автобусе, возможно, вы хотите с кем-то там встретиться или кому-то что-то передать. При этом вы можете контролировать ситуацию, провоцируя их на то, чтобы «засветиться». Вы вынуждаете их принимать неожиданные решения, при этом существует вероятность 50/50, что это решение будет в вашу пользу, они появятся слишком близко от вас, и вы их засечете.

Кроме того, если вы их вычислите, то всегда можете вызвать полицию. Тогда наблюдателям придется объяснять, какого черта они делают на полосе общественного транспорта. Кроме того, вы можете использовать естественные узкие места. Допустим, у нас есть две деревни на противоположных берегах залива. Вы можете проехать 47 миль в объезд вдоль побережья, что занимает много времени, либо использовать автомобильный паром и быстро добраться до точки назначения.

Таких мест много в озерной местности, вокруг Нью-Йорка, поэтому помните о преимуществах, которые они могут вам предоставить. В нашем случае вы поставите наблюдателей перед нелегким выбором – ехать в объезд, чтобы поймать вас на другой стороне, рискуя не успеть, или же загнать на палубу парома 1-2-3 машины наблюдения, которые вы вычислите.

Возможность применения приема обнаружения слежки под названием «А long look» операторы наблюдения вычисляют заранее с помощью карты. Это длинная, пустынная, протяженная и прямая дорога без параллельных трасс, следуя по которой достаточно посмотреть назад, чтобы обнаружить, что вас преследует конвой из нескольких оперативных машин. Наблюдатели должны избегать подобных ситуаций, поэтому слежка в городе происходит намного успешней. На снимке изображено шоссе с прямой видимостью 8 миль в одну сторону, и по нему за 4 часа проехала всего 1 машина.

SecuritySense: вы должны заставить команду преследователей принимать нестандартные управленческие решения. Например, на такой дороге они не смогут припарковать даже одну машину наблюдения, потому что не смогут выставить остальные позади вас. При этом привычная схема «плавающей коробки» ломается. Если вы придумаете, как их прижать, у преследователей произойдет разрыв шаблона.

Agent X: нужно использовать технологические преимущества. Если вы попросите кого-то помочь вам, то он сможет, например, утром у вашего дома снимать на видео каждый автомобиль, который проедет на 10 минут раньше и позже вашего. Затем в другом месте днем он повторит эту съемку. В результате вы получите 2 списка номерных знаков, сравните их, и если один или несколько номеров присутствуют в обоих списках, значит это машины команды наблюдения.
Вам может помочь технология OpenALPR. Это открытый продукт для автоматического распознавания номерных знаков автомобилей, снятых на видео.

Я покажу вам немного отснятого материала, сделанного на мой iPhone. Вы включаете камеру и снимаете автомобили, движущиеся в вашу сторону. Кадры передаются программному обеспечению, при этом в нижнем правом углу экрана в режиме реального времени появляются файлы изображений, которые передаются программному обеспечению и оно читает их кадр за кадром. Это позволит вам найти несколько изображений одного и того же номерного знака.

Используя известные технологии, мы можем запустить это приложение, извлечь его с помощью JSON и преобразовать в файл SQL. Затем мы загружаем транслятор Perl, чтобы прочитать этот файл, и получаем список номерных знаков. На экране вы видите формат английских номерных знаков, и некоторые из них выглядят очень похоже.

Это недостатки самого ПО, которое иногда не может распознать номера на размытом изображении. Но вы можете сделать стандартный счетчик SQL, который будет высчитывать количество появлений двух примерно одинаковых номеров, и тот, что встречается большее число раз, будет правильным номером.

Парковка является узким местом, вы сами выбираете, где остановиться. Преследующие вас наблюдатели вынуждены решать, могут ли они встать на той же самой парковке, потому что в этом случае вы можете их обнаружить. Особенно легко это сделать на многоуровневой парковке, которая хорошо просматривается.

Существует заблуждение, исходящее от правоохранительных структур и военных, что если вы хотите избавиться от хвоста, то должны парковаться задом, не так, как другие автомобили, чтобы при необходимости быстро стартовать с парковки. Такой способ парковки обычно бросается в глаза.

Операторы наблюдения стремятся быть как можно незаметнее, поэтому не станут парковать свою машину в обратном направлении. Однако если вы припаркуетесь таким образом перед теми, кто ведет наблюдение, то сразу обратите на себя внимание и заставите их понервничать. Обратите внимание на необычно припаркованные машины на следующих слайдах – это сразу бросается в глаза.

Еще раз напомню – если вы едете в такси, то можете остановиться где захотите, выйти из машины, и если там запрещена остановка личного транспорта или нет парковки, наблюдатели не будут знать, что им делать. Машину наблюдателей могут эвакуировать за стоянку в неположенном месте, и как они объяснят это своему начальству?

SecuritySense: дело в том, что пользуясь такси, вы можете неожиданно выйти на перекрестке и испортите им всю мобильную схему наблюдения, вынудив перейти от автомобильной слежки к пешей. В этом случае они не смогут приблизиться к вам достаточно близко и достаточно быстро. Подобный прием приведет их в замешательство и может заставить принять неверное решение.

Agent X: когда вы возвращаетесь к своей машине, я имею ввиду «узкие места», то можете как следует оглядеться и заглянуть в припаркованные рядом машины. Как и все люди, наблюдатели делают ошибки, и если вы заметите в салоне карты или устройство для радиопереговоров, наверняка этот транспорт используется для слежки. Как рассказали мне друзья, два инструктора по наблюдению, не студенты, а инструкторы, которые решили навестить своих студентов на посту скрытого наблюдения, припарковали свою автомашину и пошли пешком. Кто-то из прохожих сообщил полиции, что в машине есть что-то подозрительное, те заглянули в салон и увидели под рулевым колесом пульт рации.

Это вещь, которая нечасто встречается в машинах добропорядочных англичан, поэтому полицейские дождались возвращения инструкторов и потребовали объяснить, что они здесь делают без специальной официальной карточки, позволяющей вести наблюдение.

Когда мы представили этот доклад организаторам конференции, они спросили, почему мы решили рассказать о методах наблюдения старой школы, в то время как на сегодня существует целая куча технических средств для ведения незаметной слежки. Например, вы можете установить на машину цели GPS-трекер и отслеживать все передвижения. Конечно, это возможно, но и цель так же может использовать современные технологии для обнаружения «жучков», найдет ваш трекер и поймет, что находится под наблюдением. Да, это прекрасно – цель остановилась возле торгового центра, а вы находитесь от него на расстоянии мили и видите на карте, где он сейчас. Но как только объект выйдет из автомобиля — все, вы его потеряли. У вас нет достаточно людей, чтобы сохранять визуальный контакт с целью, поэтому вы вынуждены будете бросить свою машину, догнать его и начать преследовать пешком.

SecuritySense: смысл наблюдения не в том, чтобы узнать, куда идет человек, а в том, чтобы наблюдать за его поведением. В первую очередь наблюдателей интересует, что собирается сделать цель или что она делает сейчас. Предположим, мы знаем, что Джимми каждый вторник ходит к Фраю и покупает гамбургер, но нам важно не это, а то, с кем он встречается и делится этим гамбургером.

Agent X: рассмотрим, как обычно выглядят люди, ведущие наблюдение и на кого они похожи. По поводу этого существует куча стереотипов.

SecuritySense: СМИ приучили нас, что обычно это здоровенные крутые белые мужики с короткой стрижкой и огромными часами.

Agent X: если бы вы реально увидели на улице этих ребят, то подумали бы: «о Боже, я под наблюдением!». Во-первых, у них видны наушники, во-вторых, у левого парня поперек сорочки под галстук уходит проводок рации, в-третьих, у него под пиджаком виднеется кобура. У парня справа под рукавом рубашки имеется кнопка для ведения радиопереговоров. Если эти парни следуют за вами, то они наверняка хотят, чтобы вы чувствовали себя под наблюдением.

На следующем слайде вы видите 3-х человек, участников одного из британских телевизионных шоу. Один из них бывший сотрудник спецслужбы — Центра правительственной связи GCHQ, еще один из секретной разведки МИ6 и последний – оператор наблюдения службы безопасности МИ5. Так кто есть кто?

У них наблюдателем работает женщина, а в штате нет никаких белых здоровенных мужиков. Настоящие оперативники ничем не выделяются из толпы. Вы не обратите внимания на такого человека, если он сядет в автобусе рядом с вами.

23:45 мин

Продолжение будет совсем скоро…

Немного рекламы 🙂

Спасибо, что остаётесь с нами. Вам нравятся наши статьи? Хотите видеть больше интересных материалов? Поддержите нас, оформив заказ или порекомендовав знакомым, облачные VPS для разработчиков от $4.99, уникальный аналог entry-level серверов, который был придуман нами для Вас: Вся правда о VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps от $19 или как правильно делить сервер? (доступны варианты с RAID1 и RAID10, до 24 ядер и до 40GB DDR4).

Dell R730xd в 2 раза дешевле в дата-центре Equinix Tier IV в Амстердаме? Только у нас 2 х Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 ТВ от $199 в Нидерландах! Dell R420 — 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — от $99! Читайте о том Как построить инфраструктуру корп. класса c применением серверов Dell R730xd Е5-2650 v4 стоимостью 9000 евро за копейки?

Source: habr1

Метки:





Запись опубликована: 07.04.2020

Анализ дзета-функции Римана

В одном из разделов математики существует достаточно забавная задача про сумму чисел, связанных с натуральным рядом, и на первый взгляд кажется, что она достаточно проста, но при более глубоком погружении в тематику, приходит ощущение полной беспомощности.

Вся моя жизнь неразрывно связана с математикой. В голове постоянно рождаются мысли: «Почему именно так и какое этому объяснение?». Мне нравится находить разные способы решения интересных задач.

Так в школьные годы после темы про квадратные уравнения у меня сразу появился ряд вопросов: есть ли альтернативные варианты и как будет выглядеть решение для уравнения высших степеней?

На первый вопрос достаточно быстро был получен утвердительный ответ — да, может.
Разность корней квадратного уравнения можно выразить с помощью теоремы Виета, выполнив несложные преобразования.

Пример для квадратного уравнения

$x^2+p x+q=0$

По тереме Виета имеем

$left{ begin{array}{lcl} x_1+x_2 & = & -p  x_1 x_2 & = & q  end{array} right.$

Тогда

$left(x_1+x_2right){}^2=p^2$

$x_1^2+2 x_1 x_2+x_2^2=p^2$

$x_1^2+x_2^2+2 q=p^2$

$x_1^2+x_2^2=p^2-2 q$

$left(x_1-x_2right){}^2=x_1^2-2 x_1 x_2+x_2^2=p^2-4 q=p^2-4 q$

$x_1-x_2=pm sqrt{p^2-4 q}$

В итоге получаем систему уравнений

$left{ begin{array}{lcl} x_1+x_2 & = & -p  x_1-x_2 & = & pm sqrt{p^2-4 q}  end{array} right.$

Красиво, правда?

Кстати, а вы знали, что корни многочленов $n$-ой степени образуют поле? 😉

Дальнейшее углубление в теорию решений уравнений высших степеней открывало бесконечно много новых знаний в тех областях, о которых я даже и не подозревал.

В результате ряда рассуждений стало понятно, что существуют некоторые преобразования над набором корней уравнений, которые могут давать интересные результаты, и с их помощью можно быстро и эффективно решать уравнения для степени меньше 5.

Я узнал о теории Галуа, теореме Абеля-Руффини, и т.д.

Информацию пришлось переваривать в течение нескольких лет.

В студенческие годы на одной из скучных лекций я упражнялся с суммой различных степеней натурального ряда (до определенного значения $n$) и заметил одну закономерность, что слагаемое с максимальной степенью всегда выражается как $frac{n^{s+1}}{s+1}$.
Сразу возник вопрос: «Можно ли как-то использовать интеграл?».

Ответ не заставил себя долго ждать, и к концу пары было готово решение для любого $s$.

Я захотел получить аналогичную формулу и для отрицательных $s$, но все попытки заканчивались неудачей. Так состоялось моё первое знакомство с дзета-функцией.

Недавно мне на глаза попалась публикация о нетривиальных нулях дзета-функции Римана.
Так как доказательство гипотезы Римана является нерешенной проблемой тысячелетия, многие пытаются к ней подступиться, и периодически в разных источниках появляется информация о ее доказательстве либо опровержении. Но до сих пор ни одно из доказательств не было принято официальным математическим сообществом.

Тогда я решил разобраться и попытаться найти возможные пути решения гипотезы Римана.
Так как ранее у меня уже был опыт работы с бесконечными суммами, по наивности я решил, что это не должно быть очень сложно 😉

Что же с этой проблемой не так, если ее не могут решить на протяжении тысячелетия?
Обложившись справочным материалом, я начал вникать в проблему и изучать подходы, используемые при доказательстве. Большинство доказательств строилось на применении интегралов или специфических функций (например, функция Тодда).

На глаза попадались как совсем откровенные ляпы, так и очень сложные работы на несколько десятков страниц, погружение в которые могло занять не меньше месяца вдумчивого чтения.
Объём информации рос, а понимание, как подойти к проблеме или предположить, какой метод можно применить, чтобы приблизиться к решению, не приходило.

И тогда я решил отложить чтение профильной литературы.

Как-то часа в 3 ночи (после вечернего кофе) мне в голову пришла одна на мой взгляд очень простая и интересная последовательность действий, которая ведет к доказательству, ей я и хочу с вами поделиться.

Внесу пару уточняющих моментов:

  1. Некоторые промежуточные расчеты и выводы я намеренно опускаю, чтобы не перегружать читателя
  2. По этой же причине я намеренно опускаю ряд специфических понятий
  3. Читатель должен быть знаком с матанализом и комплексными числами
  4. Все мои рассуждения могут оказаться неверными

Итак, поехали…

Сначала определимся, что нужно доказать и что для этого у нас дано.

Необходимо доказать, что все комплексные нули дзета-функции должны иметь вид: $s_n=frac{1}{2}+mathbb{i}t,tin mathbb{R}$.

Определим, что такое дзета-функция.

Начнём наш путь с Эйлера, так как он впервые определил дзета-функцию для действительных чисел
(далее по тексту $zeta$ — функция)

$ zeta (s)=lim_{n to {infty}}(1+frac{1}{2^s}+frac{1}{3^s}+text{...}+frac{1}{n^s}), text{Re} (s)>1 $

$ zeta (s)=lim_{n to {infty}}(sum _{i=1}^n frac{1}{i^s}) $

Или просто

$ zeta (s)=sum _{i=1}^{infty } frac{1}{i^s} $

Из профильной литературы известно, что для всех выше обозначенных $s$ функция сходится абсолютно.

Также Эйлером была введена знакочередующаяся эта — функция.

(далее по тексту $eta$ — функция)

$ eta (s)=sum _{i=1}^{infty} frac{(-1)^{i-1}}{i^s} $

Бернхард Риман определил $zeta$ — функцию для комплексного переменного.

Чтобы продолжить функцию на комплексную плоскость для любого $sin mathbb{C}$, $0<text{Re} (s)<1$, проделаем пару фокусов с функцией Эйлера, разбив ее на сумму по чётным и нечётным $i$.

Тогда $zeta$ — функция будет выражаться, как сумма нечётных и чётных $i$

$ zeta (s)=sum _{i=1}^{infty } frac{1}{(2 i-1)^s}+sum _{i=1}^{infty } frac{1}{(2 i)^s} $

А $eta$ — функция будет выражаться, как разница нечётных и чётных $i$

$ eta (s)=sum _{i=1}^{infty } frac{1}{(2 i-1)^s}-sum _{i=1}^{infty } frac{1}{(2 i)^s} $

Вычтем из $zeta$ — функции $eta$ — функцию, тогда получим

$ zeta (s)-eta (s)=2 sum _{i=1}^{infty } frac{1}{(2 i)^s}=2^{1-s} sum _{i=1}^{infty } frac{1}{i^s}=2^{1-s} zeta (s) $

$ left(1-2^{1-s}right) zeta (s)=eta (s) $

$ forall sin mathbb{C},1neq text{Re} (s)>0 $

Также отмечу, что есть особые точки — нули уравнения $1-2^{1-s}=0$, которые устранимы.

Выразим $zeta$ — функцию через $eta$ — функцию

$ zeta (s)=frac{eta (s)}{1-2^{1-s}} tag{1} $

Это связь нам пригодится в дальнейшем.

Из профильной литературы известно, что в нулях $eta$ — функции $zeta$ — функция также обращается в нуль.

Из формулы Эйлера — Маклорена, следует, что при $n to {infty}$
(далее по тексту равенства с $n$ будут рассматриваться, как предел при $n to {infty}$)

$ zeta (s)=sum _{i=1}^n frac{1}{i^s}-frac{n^{1-s}}{1-s},sin mathbb{C},Re(s)>0 tag{2} $

Или

$ zeta (s)=sum _{i=1}^{2 n} frac{1}{i^s}-frac{(2 n)^{1-s}}{1-s}=sum _{i=1}^n frac{1}{(2 i-1)^s}+sum _{i=1}^n frac{1}{(2 i)^s}-frac{(2 n)^{1-s}}{1-s} $

Выразим $zeta$ — функцию через чётные $i$ и $n$

$ zeta (s)=frac{2^s}{2^s} sum _{i=1}^n frac{1}{i^s}-frac{2^{1-s} n^{1-s}}{2^{1-s} (1-s)}=2^s sum _{i=1}^n frac{1}{(2 i)^s}-frac{(2 n)^{1-s}}{2^{1-s} (1-s)}= $

$ 2^s sum _{i=1}^n frac{1}{(2 i)^s}-frac{2^s (2 n)^{1-s}}{2 (1-s)}=2^s left(sum _{i=1}^n frac{1}{(2 i)^s}-frac{(2 n)^{1-s}}{2 (1-s)}right) $

Или

$ 2^{-s} zeta (s)=sum _{i=1}^n frac{1}{(2 i)^s}-frac{(2 n)^{1-s}}{2 (1-s)} tag{3} $

Выразим $zeta$ — функцию через нечётные $i$ и $n$

$ zeta (s)=sum _{i=1}^n frac{1}{(2 i-1)^s}+sum _{i=1}^n frac{1}{(2 i)^s}-frac{(2 n)^{1-s}}{1-s}= $

$ sum _{i=1}^n frac{1}{(2 i-1)^s}-frac{(2 n)^{1-s}}{1-s}+frac{(2 n)^{1-s}}{2 (1-s)}+2^{-s} zeta (s)= $

$ sum _{i=1}^n frac{1}{(2 i-1)^s}-frac{(2 n)^{1-s}}{2 (1-s)}+2^{-s} zeta (s) $

Или

$ sum _{i=1}^n frac{1}{(2 i-1)^s}=frac{(2 n)^{1-s}}{2 (1-s)}-2^{-s} zeta (s)+zeta (s)=frac{(2 n)^{1-s}}{2 (1-s)}+left(1-2^{-s}right) zeta (s) $

Или

$ left(1-2^{-s}right) zeta (s)=sum _{i=1}^n frac{1}{(2 i-1)^s}-frac{(2 n)^{1-s}}{2 (1-s)} tag{4} $

Заметим, что

$ frac{(2 n)^{1-s}}{(2 n-1)^{1-s}}=1 $

Тогда, используя (4), запишем

$ left(1-2^{-s}right) zeta (s)=sum _{i=1}^n frac{1}{(2 i-1)^s}-frac{(2 n-1)^{1-s}}{2 (1-s)} $

Тогда в нулях
(далее по тексту это выражение будет часто употребляться, в нулях означает, что $s$ — нуль $zeta$ — функции)

$ sum _{i=1}^n frac{1}{(2 i-1)^s}=frac{(2 n-1)^{1-s}}{2 (1-s)} tag{5} $

И

$ sum _{i=1}^n frac{1}{(2 i)^s}=frac{(2 n)^{1-s}}{2 (1-s)} tag{6} $

Используя (5) (6), заметим, что в нулях

$ eta (s)=frac{(2 n-1)^{1-s}}{2 (1-s)}-frac{(2 n)^{1-s}}{2 (1-s)} tag{7} $

Используя (7), заметим, что

$ frac{(2 n-1)^{1-s}}{2 (1-s)}-frac{(2 n)^{1-s}}{2 (1-s)}=-frac{1}{2 (2 n)^s} tag{8} $

Тогда, используя (8), в нулях можно записать равенство

$ eta (s)=-frac{1}{2 (2 n)^s} tag{9} $

Из профильной литературы известно, что $forall s$

$ pi ^{-frac{s}{2}} zeta (s) Gamma left(frac{s}{2}right)=pi ^{-frac{1}{2} (1-s)} zeta (1-s) Gamma left(frac{1-s}{2}right) $

Где $Gamma left(sright)$ — гамма-функция Эйлера.

Или, используя (1)

$ frac{pi ^{-frac{s}{2}} eta (s) Gamma left(frac{s}{2}right)}{1-2 2^{-s}}=frac{pi ^{-frac{1}{2} (1-s)} eta (1-s) Gamma left(frac{1-s}{2}right)}{1-2 2^{-(1-s)}} $

Тогда

$ frac{pi ^{-frac{s}{2}} eta (s) Gamma left(frac{s}{2}right)}{frac{left(1-2 2^{-s}right) left(pi ^{-frac{1}{2} (1-s)} eta (1-s) Gamma left(frac{1-s}{2}right)right)}{1-2 2^{-(1-s)}}}=1 $

Используя (9), запишем равенство в нулях

$ frac{pi ^{-frac{s}{2}} Gamma left(frac{s}{2}right)}{-frac{left(left(1-2 2^{-s}right) left(2 (2 n)^sright)right) left(pi ^{-frac{1}{2} (1-s)} Gamma left(frac{1-s}{2}right)right)}{left(1-2 2^{-(1-s)}right) left(2 (2 n)^{1-s}right)}}=1 tag{10} $

Тогда в нулях должно также выполняться равенство

$ left(frac{pi ^{-frac{s}{2}} Gamma left(frac{s}{2}right)}{-frac{left(left(1-2 2^{-s}right) left(2 (2 n)^sright)right) left(pi ^{-frac{1}{2} (1-s)} Gamma left(frac{1-s}{2}right)right)}{left(1-2 2^{-(1-s)}right) left(2 (2 n)^{1-s}right)}}right)^2=1 $

Упростим выражение и запишем его в следующем виде

$ left(-frac{4^{1-s} left(2^s-1right) pi ^{-s} n^{1-2 s} cos left(frac{pi s}{2}right) Gamma (s)}{2^s-2}right)^2=1 tag{11} $

Положим $s=sigma +mathbb{i}t,sigma in mathbb{R},tin mathbb{R}$ и запишем модули каждого из сомножителей

$ left 4^{1-s}right =4^{1-sigma } $

$ left 2^s-1right =sqrt{4^{sigma }-2^{sigma +1} cos (t log (2))+1} $

$ left pi ^{-s}right =pi ^{-sigma } $

$ left n^{1-2 s}right =n^{1-2 sigma } $

$ left cos left(frac{pi s}{2}right) Gamma (s)right to left sqrt{frac{pi }{2}} left(sqrt{t^2+sigma ^2}right)^{sigma -frac{1}{2}}right, sigma in [0,1], forall tin mathbb{R} $

$ left 2^s-2right =sqrt{4^{sigma }-2^{sigma +2} cos (t log (2))+4} $

Тогда перепишем (11) в следующем виде

$ left(frac{sqrt{frac{pi }{2}} 4^{1-sigma } pi ^{-sigma } n^{1-2 sigma } left(sqrt{sigma ^2+t^2}right)^{sigma -frac{1}{2}} sqrt{4^{sigma }-2^{sigma +1} cos (t log (2))+1}}{sqrt{4^{sigma }-2^{sigma +2} cos (t log (2))+4}}right)^2=1 $

Или

$ frac{2^{3-4 sigma } pi ^{1-2 sigma } n^{2-4 sigma } left(sigma ^2+t^2right)^{sigma -frac{1}{2}} left(4^{sigma }-2^{sigma +1} cos (t log (2))+1right)}{4^{sigma }-2^{sigma +2} cos (t log (2))+4}=1 tag{12} $

Заметим, что (12) представляет собой периодическую функцию, верхняя и нижняя границы которой будут равны

$ alpha (sigma ,t)=frac{2^{3-4 sigma } pi ^{1-2 sigma } left(4^{sigma }-2^{sigma +1}+1right) n^{2-4 sigma } left(sigma ^2+t^2right)^{sigma -frac{1}{2}}}{4^{sigma }-2^{sigma +2}+4} $

$ beta (sigma ,t)=frac{2^{3-4 sigma } pi ^{1-2 sigma } left(4^{sigma }+2^{sigma +1}+1right) n^{2-4 sigma } left(sigma ^2+t^2right)^{sigma -frac{1}{2}}}{4^{sigma }+2^{sigma +2}+4} $

Для того, чтобы выражение (12) для $forall n$ было равно 1, нужно, чтобы верхняя и нижняя граница для $forall n$ были равны 1.

Из профильной литературы известно, что любой нетривиальный нуль $zeta$ — функции имеет действительную часть $sigma in (0,1)$.

Тогда запишем варианты пределов для верхней и нижней границ при $sigma in (0,1)$, $tin (-infty ,+infty )$

$ begin{cases} при sigma in left(0,frac{1}{2}right), alpha (sigma ,t)=infty,beta (sigma ,t)=infty  при sigma =frac{1}{2}, alpha (sigma ,t)=1,beta (sigma ,t)=1  при sigma in left(frac{1}{2},1right), alpha (sigma ,t)=0,beta (sigma ,t)=0  end{cases} $

Как видно, нам подходит только вариант $sigma =frac{1}{2}$ и только в этом случае возможно соблюдение равенства (11) в нулях.

Следовательно, все комплексные нули $zeta$ — функции имеют вид: $s_n=frac{1}{2}+mathbb{i}t,tin mathbb{R}$.

Что и требовалось доказать.

Теги:
Хабы:


Source: habr1

Метки:





Запись опубликована: 07.04.2020

Про фразовые глаголы-2

Те, кто в теме, знают, сколько копий ломают в Интернете сами носители английского по поводу фразовых глаголов. Поэтому прошу воспринимать мои статьи на эту тему просто как рассуждения неглупого русскоязычного преподавателя с серьёзным стажем теоретической и практической работы и опытом длительного проживания в языковой среде. Где-то я могу ошибаться – поправляйте. Желательно в корректной форме.

Для начала договоримся о терминах. Англо-английские словари содержат глаголы, состоящие из трёх частей (в предыдущей статье я упоминал let in on). Но, строго говоря, третья маленькая часть в них – всегда просто обычный предлог и в состав фразового глагола не входит, мы это разберём попозже. Будем исходить из того, что фразовый глагол состоит всегда из 2-х частей: собственно глагола и второй маленькой части (in, up, off и т.д.), которая может быть тремя разными вещами: предлогом, наречием, показывающим направление или частицей. И вот по тому, в какой роли выступает эта вторая мелкая часть, фразовые глаголы делятся на 3 типа. (Разные авторы используют разную терминологию и классификации, но давайте пока остановимся на предложенных.)

Я начну с 3-го типа, в котором эта мелкая часть является предлогом. Очень многие авторы относят к фразовым глаголам то, что, по сути, является обычным глаголом с определённым управлением. Управление глагола – это то, как глагол употребляется: с предлогом, без предлога, если с предлогом, то с каким. Допустим, одно из управлений русского глагола «смотреть» требует после себя предлога «на», а после «на» должен идти обязательно винительный падеж, потому что смотрят на кого/что. Другой падеж не подойдёт, иначе будут получаться предложения в стиле «я твой труба шатал». Шатают в русском языке кого/что. У глаголов бывает, как правило, несколько управлений. Допустим, у глагола «смотреть» есть другое управление, которое требует после себя предлога «за» (за ребёнком, например, или пожилым человеком) и после предлога «за» должен идти творительный падеж, потому что смотрят за кем/чем.

Глагол depend («зависеть») требует после себя предлога on. Depend on – «зависеть ОТ». Тут просматривается вполне понятная логика. Pend – устаревшее слово «висеть». Висеть можно НА чём-то. Pend on – «висеть НА». Авторы популярного пособия по фразовым глаголам «English Phrasal Verbs In Use» считают depend on фразовым, в то время как, по сути, это обычный глагол, который требует после себя предлога «on» – такое у него управление. «On» здесь – именно предлог, а не наречие и не частица. Считать его фразовым – всё равно, что назвать фразовым глаголом русскую конструкцию «зависеть от». В русском языке вообще нет фразовых глаголов.

Или возьмём более сложный пример – look for. Look for означает «искать». На самом деле это не фразовый глагол, а обычный глагол look – «смотреть», который в одном из своих управлений требует предлога for. For означает «для». Т.е. не просто смотреть (делать тебе нефига – сидишь смотришь), а смотреть ДЛЯ чего-то, С КАКОЙ-ТО ЦЕЛЬЮ.

Или: Tesla runs on electricity – Тесла работает на электричестве. Обычный глагол run с управлением on, а учебники почему-то часто называют run on фразовым.

2-й тип глаголов, которые традиционно относят к фразовым – это глаголы, в которых мелкая часть является тоже не частицей, а полноценным наречием, обозначающим направление. Например, bring up. Bring – «приносить», up – «наверх». Семья ужинает на 2-м этаже дома, ребёнку командуют: bring the dishes up – принеси тарелки наверх. Здесь up в прямом смысле означает «наверх». Конечно, bring up используется в переносных значениях. Например, bring up может означать «воспитывать». Но! «Воспитывать» – это просто расширенное, метафорическое значение буквального «приносить наверх». Ребёнок был маленький, родители через определённые действия как бы принесли его наверх, теперь большой. Так что bring up в значении «воспитывать», пожалуй, скорее идиома, чем фразовый глагол.

То же самое с глаголом look forward. Look forward буквально означает «смотреть вперёд», его расширенное, метафорическое значение – «ожидать с нетерпением». Например, I am looking forward to your visit – Я с нетерпением ожидаю вашего визита. Или «смотрю вперёд, в будущее, в котором состоится ваш визит». Так что приходится признать, что look forward – это, пожалуй, тоже скорее идиома, чем фразовый глагол. Так что отнесём его ко 2-му типу фразовых глаголов.

И, наконец, 1-й тип – собственно то, что и должно в строгом смысле называться фразовыми глаголами. В них вторая часть является не предлогом и не наречием, а в строгом смысле частицей. И эти частицы уже выполняют ту самую функцию, которую в русском обычно выполняют приставки.

Ко многим глаголам понятие «направления» попросту неприменимо. Например, drink. Нельзя пить вперёд или назад. У глагола drink up никогда не было буквального значения «пить наверх», он образован именно как фразовый. Up в нём – именно частица, несущая смысл «до конца, полностью» (соответствует русской приставке «до-» с тем же значением), поэтому drink up означает «до-пить». Этим и отличаются В СТРОГОМ СМЫСЛЕ ФРАЗОВЫЕ глаголы от идиом (2-й тип) – у идиом первоначально был буквальный смысл, а их современное значение – просто метафорическая надстройка.

– Слушай, ты и учишься, и работаешь, и ещё в Студсовете что-то делать успеваешь! Как ты высыпаешься?

– Куда высыпаюсь?

– Понятно.

Вот точно так же в одном из своих значений фразовый глагол может относиться к 1-му типу, а в другом – ко 2-му.

Разница между 1-м и 2-м типом фразовых глаголов очень тонкая. В некоторых случаях разглядеть её могут только профессиональные лингвисты и при этом носители языка. А порой, может быть, и у них мнения не сойдутся, как, например, с некоторыми фразовыми глаголами, происхождение и буквальное значение которых потеряно в веках.

В дальнейших статьях я буду рассматривать только 1-й и 2-й типы. 3-й – вообще не фразовые глаголы и никаких особых проблем с ними у студентов не возникает.

image

Теги:
Хабы:


Source: habr1

Метки:





Запись опубликована: 07.04.2020

[Из песочницы] Doctrine ResultSetMapping на примерах

Doctrine ORM предоставляет разработчику удобные средства выборки данных. Это и мощный DQL для работы в объектно-ориентированном ключе, и удобный Query Builder, простой и понятный в использовании. Они покрывают большую часть потребностей, но иногда возникает необходимость использовать SQL запросы, оптимизированные или специфичные для конкретной СУБД. Для работы с результатами запросов в коде важно понимание того, как работает маппинг в Doctrine.

В основе Doctrine ORM лежит паттерн Data Mapper, изолирующий реляционное представление от объектного, и конвертирующий данные между ними. Одним из ключевых компонентов этого процесса является объект ResultSetMapping, с помощью которого описывается, как именно преобразовывать результаты запроса из реляционной модели в объектную. Doctrine всегда использует ResultSetMapping для представления результатов запроса, но обычно этот объект создается на основе аннотаций или yaml, xml конфигов, остается скрыт от глаз разработчика, потому о его возможностях знают далеко не все.

Для примеров работы с ResultSetMapping я буду использовать MySQL и employees sample database.

Структура БД

Опишем сущности Department, Employee, Salary, c которыми и будем продолжать работу далее.

Deparment

<?php
namespace AppEntity;
use DoctrineORMMapping as ORM;
/**
 * Departments
 *
 * @ORMTable(name="departments", uniqueConstraints={@ORMUniqueConstraint(name="dept_name", columns={"dept_name"})})
 * @ORMEntity
 */
class Department
{
    /**
     * @var string
     *
     * @ORMColumn(name="dept_no", type="string", length=4, nullable=false, options={"fixed"=true})
     * @ORMId
     * @ORMGeneratedValue(strategy="IDENTITY")
     */
    private $deptNo;
    /**
     * @var string
     *
     * @ORMColumn(name="dept_name", type="string", length=40, nullable=false)
     */
    private $deptName;
    /**
     * @var DoctrineCommonCollectionsCollection
     *
     * @ORMManyToMany(targetEntity="Employee", mappedBy="deptNo")
     */
    private $empNo;
    /**
     * Constructor
     */
    public function __construct()
    {
        $this->empNo = new DoctrineCommonCollectionsArrayCollection();
    }
}

Employee

<?php

namespace AppEntity;

use DoctrineORMMapping as ORM;

/**
* Employees
*
* ORMTable(name=«employees»)
* ORMEntity
*/
class Employee
{
/**
* var int
*
* ORMColumn(name=«emp_no», type=«integer», nullable=false)
* ORMId
* ORMGeneratedValue(strategy=«IDENTITY»)
*/
private $empNo;

/**
* var DateTime
*
* ORMColumn(name=«birth_date», type=«date», nullable=false)
*/
private $birthDate;

/**
* var string
*
* ORMColumn(name=«first_name», type=«string», length=14, nullable=false)
*/
private $firstName;

/**
* var string
*
* ORMColumn(name=«last_name», type=«string», length=16, nullable=false)
*/
private $lastName;

/**
* var string
*
* ORMColumn(name=«gender», type=«string», length=0, nullable=false)
*/
private $gender;

/**
* var DateTime
*
* ORMColumn(name=«hire_date», type=«date», nullable=false)
*/
private $hireDate;

/**
* var DoctrineCommonCollectionsCollection
*
* ORMManyToMany(targetEntity=«Department», inversedBy=«empNo»)
* ORMJoinTable(name=«dept_manager»,
* joinColumns={
* ORMJoinColumn(name=«emp_no», referencedColumnName=«emp_no»)
* },
* inverseJoinColumns={
* ORMJoinColumn(name=«dept_no», referencedColumnName=«dept_no»)
* }
* )
*/
private $deptNo;

/**
* Constructor
*/
public function __construct()
{
$this->deptNo = new DoctrineCommonCollectionsArrayCollection();
}

}

Salary

<?php

namespace AppEntity;

use DoctrineORMMapping as ORM;

/**
* Salaries
*
* ORMTable(name=«salaries», indexes={@ORMIndex(name=«IDX_E6EEB84BA2F57F47», columns={«emp_no»})})
* ORMEntity
*/
class Salary
{
/**
* var DateTime
*
* ORMColumn(name=«from_date», type=«date», nullable=false)
* ORMId
* ORMGeneratedValue(strategy=«NONE»)
*/
private $fromDate;

/**
* var int
*
* ORMColumn(name=«salary», type=«integer», nullable=false)
*/
private $salary;

/**
* var DateTime
*
* ORMColumn(name=«to_date», type=«date», nullable=false)
*/
private $toDate;

/**
* var Employee
*
* ORMId
* ORMOneToOne(targetEntity=«Employee»)
* ORMJoinColumns({
* ORMJoinColumn(name=«emp_no», referencedColumnName=«emp_no»)
* })
*/
private $empNo;

/**
* var Employee
*
*/
private $employee;

}

Entity result

Начнем с простого, выберем все департаменты из базы и спроецируем их на Department:

        $rsm = new QueryResultSetMapping();
        $rsm->addEntityResult(Department::class, 'd');
        $rsm->addFieldResult('d', 'dept_no', 'deptNo');
        $rsm->addFieldResult('d', 'dept_name', 'deptName');
        $sql = 'SELECT * FROM departments';
        $query = $this->entityManager->createNativeQuery($sql, $rsm);
        $result = $query->getResult();

Метод addEntityResult указывает, на какой класс будет проецироваться наша выборка, а методы addFieldResult указывают на сопоставление столбцов выборки и полей объектов. SQL запрос, передаваемый в метод createNativeQuery будет передан в БД именно в таком виде и Doctrine не будет никаким образом его изменять.

Стоит помнить, что Entity обязательно обладает уникальным идентификатором, который должен быть включен в fieldResult.

Joined Entity result

Выберем департаменты, в которых присутствуют сотрудники, нанятые после начала 2000 года, вместе с этими сотрудниками.

        $rsm = new QueryResultSetMapping();
        $rsm->addEntityResult(Department::class, 'd');
        $rsm->addFieldResult('d', 'dept_no', 'deptNo');
        $rsm->addFieldResult('d', 'dept_name', 'deptName');
        $rsm->addJoinedEntityResult(Employee::class, 'e', 'd', 'empNo');
        $rsm->addFieldResult('e', 'first_name', 'firstName');
        $rsm->addFieldResult('e', 'last_name', 'lastName');
        $rsm->addFieldResult('e', 'birth_date', 'birthDate');
        $rsm->addFieldResult('e', 'gender', 'gender');
        $rsm->addFieldResult('e', 'hire_date', 'hireDate');
        $sql = "
        SELECT *
            FROM departments d
            JOIN dept_emp ON d.dept_no = dept_emp.dept_no
            JOIN employees e on dept_emp.emp_no = e.emp_no
        WHERE e.hire_date > DATE ('1999-12-31')
       ";
        $query = $this->entityManager->createNativeQuery($sql, $rsm);
        $result = $query->getResult();

ResultSetMappingBuilder

Как видно, работа с непосредственно объектом ResultSetMapping несколько сложна и заставляет крайне детально описывать сопоставление выборки и объектов. К частью, Doctrine предоставляет более удобный инструмент — ResultSetMappingBuilder, являющийся оберткой над RSM и добавляющий больше удобных методов для работы с маппингом. Например, метод generateSelectClause, позволяющий создать параметр для SELECT части запроса с описанием необходимых для выборки полей. Предыдущий запрос можно переписать в более простом виде.

        $sql = "
        SELECT {$rsm->generateSelectClause()}
            FROM departments d
            JOIN dept_emp ON d.dept_no = dept_emp.dept_no
            JOIN employees e on dept_emp.emp_no = e.emp_no
        WHERE e.hire_date > DATE ('1999-12-31')
       ";

Стоит обратить внимание, что если не указать все поля создаваемого объекта, то Doctrine вернет partial object, использование которых может быть оправдано в некоторых случаях, но их поведение опасно и не рекомендуется. Вместо этого, ResultSetMappingBuilder позволяет не указывать каждое поле итогового класса, а использовать описанные через аннотации (yaml, xml конфигурации) сущности. Перепишем наш RSM из предыдущего запроса с учетом этих методов:

        $rsm = new QueryResultSetMappingBuilder($this->entityManager);
        $rsm->addRootEntityFromClassMetadata(Department::class, 'd');
        $rsm->addJoinedEntityFromClassMetadata(Employee::class, 'e', 'd', 'empNo');

Scalar result

Повсеместное использование описанных entity не оправдано, может привести к проблемам с производительностью, так как Doctrine требуется создать множество объектов, которые в дальнейшем не будут использоваться полноценно. Для таких случаев предоставляется инструмент маппинга скалярных результатов — addScalarResult.

Выберем среднюю зарплату по каждому департаменту:

        $rsm = new ResultSetMappingBuilder($this->entityManager);
        $rsm->addScalarResult('dept_name', 'department', 'string');
        $rsm->addScalarResult('avg_salary', 'salary', 'integer');
        $sql = "
            SELECT d.dept_name, AVG(s.salary) AS avg_salary
            FROM departments d
            JOIN dept_emp de on d.dept_no = de.dept_no
            JOIN employees e on de.emp_no = e.emp_no
            JOIN salaries s on e.emp_no = s.emp_no
            GROUP BY d.dept_name
        ";
        $query = $this->entityManager->createNativeQuery($sql, $rsm);
        $result = $query->getResult();

Первым аргументом метода addScalarResult служит имя колонки в результирующей выборке, вторым — ключ массива результатов, которые вернет Doctrine. Третий параметр — тип значения результата. Возвращаемым значением всегда будет массив массивов.

Маппинг на DTO

Но работать с массивами, особенно когда те имеют сложную структуру не очень удобно. Нужно держать в голове названия ключей, типы полей. В Doctrine DQL есть возможность использовать в результатах выборки простые DTO, например:

    SELECT NEW DepartmentSalary(d.dept_no, avg_salary) FROM …

А что же в случае Native Query и RSM? Doctrine не предоставляет документированных возможностей для создания новых DTO, но путем использования свойства newObjectMappings мы можем указать объекты, в которые хотим маппить результаты выборки. В отличие от Entity, эти объекты не будут контролироваться UnitOfWork’ом, и не обязаны находиться в неймспейсах, указанных в конфигурации.

Дополним RSM из предыдущего примера:

        $rsm->newObjectMappings['dept_name'] = [
            'className' => DepartmentSalary::class,
            'argIndex' => 0,
            'objIndex' => 0,
        ];
        $rsm->newObjectMappings['avg_salary'] = [
            'className' => DepartmentSalary::class,
            'argIndex' => 1,
            'objIndex' => 0,
        ];

Ключ в массиве поля newObjectMappings указывает на столбец результирующий выборки, значением же его является другой массив, в котором описывается создаваемый объект. Ключ className определяет имя класса нового объекта, argIndex — порядок аргумента в конструкторе объекта, objIndex — порядок объекта, если мы хотим получить несколько объектов из каждой строки выборки.

Meta result

Meta result используется для получения мета-данных колонок, таких как foreign keys или discriminator columns. К сожалению, я не смог придумать пример на основе employees базы данных, потому придется ограничиться описанием и примерами из документации.

        $rsm = new ResultSetMapping;
        $rsm->addEntityResult('User', 'u');
        $rsm->addFieldResult('u', 'id', 'id');
        $rsm->addFieldResult('u', 'name', 'name');
        $rsm->addMetaResult('u', 'address_id', 'address_id');
        $query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);

В данном случае, методом addMetaResult мы указываем Doctine, что таблица users имеет внешний ключ на addresses, но вместо того, чтобы загружать ассоциацию в память (eager load), мы создаем proxy-object, который хранит идентификатор сущности, и при обращении к нему загрузит ее из БД.

Классические реляционные базы данных не предлагают механизмов наследования таблиц, в то же время в объектной модели наследование широко распространено. Результат нашей выборки может проецироваться на иерархию классов, по некоему признаку, являющемуся значением колонки <discriminator_column> в результирующей выборке. В этом случае, мы можем указать RSM, по какой колонке Doctrine должна определять инстанцируемый класс посредством метода setDiscriminatorColumn.

Заключение

Doctrine весьма богата различными возможностями, в том числе и такими, о которых знают не все разработчики. В данном посте я постарался ознакомить с пониманием работы одного из ключевых компонентов ORM — ResultSetMapping в комбинации с Native Query. Использовать действительно сложные и платформо-зависимые запросы, сохранив доступность и понятность примеров было бы сложной задачей, потому акцент сделан именно на понимание работы ResultSetMapping. После этого вы сможете использовать его в действительно сложных запросах к вашим базам данных.

Теги:
Хабы:


Source: habr1

Метки:





Запись опубликована: 07.04.2020

[Перевод] Сортировка в Scala — пример на кошках

  • Перевод

Привет, Хабр! Выношу на ваш суд русскоязычный перевод моей статьи на Medium: Sorting in Scala — a cat shop example. Статья рассчитана на читателей, знающих синтаксис языка Scala и осведомлённых о базовых инструментах стандартной библиотеки.

Несмотря на то, что и Java, и Scala используют JVM в качестве runtime-платформы, Scala получила известность как гораздо более выразительный язык, благодаря значительной адаптации концепций функционального программирования и богатой стандартной библиотеке. В этой статье я рассмотрю пример данной выразительности, попытавшись представить, как небольшой участок кодовой базы и соответствующие требования могут эволюционировать с течением времени.

Первоначальная постановка задачи

Представим, что в нашем распоряжении находится магазин кошек (поскольку кошки — самое популярное животное в Scala-экосистеме). Из-за особенностей получения информации о кошках, доступных к продаже, в некоторых случаях информация поступает не из базы данных и должна быть отсортирована вручную перед отправкой HTTP ответа клиенту или обработкой её каким-либо другим образом. Основным объектом предметной области является, конечно, Cat, который в начале обладает только тремя полями примитивных типов. Задачей является разработать API для сортировки коллекций кошек, удовлетворяющий следующим требованиям:

  • Порядок сортировки может быть определён по каждому из полей
  • Порядок сортировки может быть не определён для любого из полей
  • Сортировка должна быть стабильной для полей с неопределённым порядком сортировки
  • Каждое поле имеет предопределённый приоритет при сортировке (т.е. сортировка по полю age будет всегда приоритетнее сортировки по полю name)

Первая итерация

case class Cat(age: Int,
               name: String,
               available: Boolean)

Поскольку постановка задачи относительно проста, а информация о других частях проекта отсутствует, первоначальное решение лучше сделать простым. К счастью, scala.Ordering уже определяет удобный метод Tuple3, с помощью которого можно создавать экземпляры Ordering для кортежей размерности 3, к которым легко приводится любой объект класса Cat.

Тем не менее, взаимодействовать с Tuple3 напрямую несколько затруднительно, поскольку данный метод требует явного предоставления сортировки при необходимости изменения порядка. Более того, отсутствует возможность удобного исключения полей из сортировки — необходимо либо переключаться между методами Tuple1, Tuple2, Tuple3, либо явно предоставлять для игнорируемых полей тождественный объект Ordering, не меняющий исходного порядка элементов. Всего нужно будет рассмотреть 9 случаев (3 возможных порядка сортировки и 3 поля), поскольку приоритет сортировки предопределён.

Для того, чтобы упростить API и приблизить его к предметной области, введём «порядок сортировки» как сущность. Согласно требованиям, существует всего 3 порядка: по возрастанию (естественный), по убыванию (обратный естественному) и «отсутствующий» (сохраняет исходный порядок). Это естественным образом отображается в следующий алгебраический тип данных (ADT):

sealed trait SortOrder
object SortOrder {
  case object Keep extends SortOrder
  case object Asc extends SortOrder
  case object Desc extends SortOrder
}

Необходимо также определить способ применения порядка сортировки к Ordering. Поскольку SortOrder выглядит как класс, который можно активно переиспользовать, данную функциональность лучше всего вынести как синтасическое расширение, чтобы она не пересекалась с определением класса:

import common.OrderingUtil
import iteration1.SortOrder.{Asc, Desc, Keep}
object syntax {
  implicit class OrderSyntax(val order: SortOrder) extends AnyVal {
    def apply[A](ordering: Ordering[A]): Ordering[A] =
      order match {
        case Keep => OrderingUtil.identity
        case Asc => ordering
        case Desc => ordering.reverse
      }
  }
}

OrderingUtil.identity — вспомогательная функция, предоставляющая тождественный порядок сортировки для любого типа A, сохраняющий порядок элементов. Она определена как: Ordering.by(_ => 0).

Теперь, когда основные элементы определены, остаётся только создать непосредственно Ordering[Cat]. С этой целью создан модуль CatOrdering:

import iteration1.syntax._
object CatOrdering {
  def of(idOrder: SortOrder,
         nameOrder: SortOrder,
         availableOrder: SortOrder): Ordering[Cat] =
    Ordering
      .Tuple3(idOrder(Ordering.Int), nameOrder(Ordering.String), availableOrder(Ordering.Boolean))
      .on[Cat](cat => (cat.age, cat.name, cat.available))
}

Теперь любой необходимый порядок сортировки (Ordering[Cat]) может быть создан при помощи функции CatOrdering.of:

CatOrdering.of(SortOrder.Asc, SortOrder.Keep, SortOrder.Desc)

Поскольку число тестовых случаев относительно велико, для проверки корректности реализации можно использовать комбинацию ScalaTest и ScalaCheck с целью написания property-based тестов. Это позволит автоматически генерировать коллекции, для которых можно проверить корректность заданного порядка сортировки. Набор тестов для первой итерации доступен по ссылке.

Вторая итерация

С момента первой итерации магазин вырос и в классе Cat появилось новое опциональное поле.

case class Cat(age: Int,
               name: String,
               available: Boolean,
               owner: Option[String])

Основное отличие от примитивных типов в том, что появился особый случай — пустое значение (не имеет ничего общего с null!), которое, в зависимости от предпочтений, может считаться либо максимальным значением, либо минимальным. Это означает, что существует 4 возможных порядка сортировки по этому полю:

  1. По возрастанию, пустые значения в начале (естественный)
  2. По возрастанию, пустые значения в конце
  3. По убыванию, пустые значения в начале
  4. По убыванию, пустые значения в конце (обратный естественному)

В то время как два из них (1 и 4) можно получить при помощи scala.Ordering.Option, оставшиеся 2 должны быть заданы явно. Поскольку SortOrder теперь ответственен за работу с пустыми значениями, он явно определяет их приоритет:

sealed trait SortOrder
object SortOrder {
  case class Asc(emptyFirst: Boolean) extends SortOrder
  case class Desc(emptyFirst: Boolean) extends SortOrder
  case object Keep extends SortOrder
  object Asc {
    def emptyFirst: Asc = Asc(emptyFirst = true)
    def emptyLast: Asc = Asc(emptyFirst = false)
  }
  object Desc {
    def emptyFirst: Desc = Desc(emptyFirst = true)
    def emptyLast: Desc = Desc(emptyFirst = false)
  }
}

Соответствующие изменения произведены и для синтаксического расширения SortOrder. Метод optional предоставляет Ordering[Option[A]] для любого A, вместе с тем применяя правило для пустых значений, определённое в SortOrder. Стоит отметить, что, хотя Ordering[Option[A]] можно также создать и с помощью метода apply, для этого необходимо явно предоставить Ordering[Option[A]], что исключает возможность сделать это случайно. Данное небольшое противоречие можно устранить предоставлением доказательства, что A в apply не является наследником Option. Для изучения инструментов возможной реализации можно ознакомиться с документацией класса <:<, и данным вопросом на StackOverflow (в Dotty Not доступен как часть стандартной библиотеки).

import common.OrderingUtil
import iteration2.sort_order.SortOrder._
object syntax {
  private object OptionOrdering {
    def apply[A](rootOrdering: Ordering[A],
                 emptyFirst: Boolean): Ordering[Option[A]] =
      if (emptyFirst)
        OptionOrdering.emptyFirst(rootOrdering)
      else
        OptionOrdering.emptyLast(rootOrdering)
    def emptyFirst[A](rootOrdering: Ordering[A]): Ordering[Option[A]] =
      (x: Option[A], y: Option[A]) => (x, y) match {
        case (None, None) => 0
        case (None, _) => -1
        case (_, None) => 1
        case (Some(a), Some(b)) => rootOrdering.compare(a, b)
      }
    def emptyLast[A](rootOrdering: Ordering[A]): Ordering[Option[A]] =
      (x: Option[A], y: Option[A]) => (x, y) match {
        case (None, None) => 0
        case (None, _) => 1
        case (_, None) => -1
        case (Some(a), Some(b)) => rootOrdering.compare(a, b)
      }
  }
  implicit class OrderSyntax(val order: SortOrder) extends AnyVal {
    def optional[A](ordering: Ordering[A]): Ordering[Option[A]] =
      order match {
        case Keep => OrderingUtil.identity
        case Asc(emptyFirst) => OptionOrdering(ordering, emptyFirst)
        case Desc(emptyFirst) => OptionOrdering(ordering.reverse, emptyFirst)
      }
    def apply[A](ordering: Ordering[A]): Ordering[A] =
      order match {
        case Keep => OrderingUtil.identity
        case Asc(_) => ordering
        case Desc(_) => ordering.reverse
      }
  }
}

import iteration2.sort_order.SortOrder
import iteration2.sort_order.syntax._
import scala.Ordering.{Boolean => BooleanO, Int => IntO, String => StringO}
object CatOrdering {
  def toOrdering(idOrder: SortOrder,
                 nameOrder: SortOrder,
                 availableOrder: SortOrder,
                 ownerOrder: SortOrder): Ordering[Cat] = {
    Ordering
      .Tuple4(idOrder(IntO), nameOrder(StringO), availableOrder(BooleanO), ownerOrder.optional(StringO))
      .on[Cat](cat => (cat.age, cat.name, cat.available, cat.owner))
  }
}

Пример создания Ordering[Cat] приведён ниже. Необходимо заметить, что для обязательных полей всё равно необходимо предоставлять правило сортировки пустых значений.

CatOrdering.toOrdering(
  SortOrder.Asc.emptyFirst,
  SortOrder.Asc.emptyFirst,
  SortOrder.Asc.emptyFirst,
  SortOrder.Asc.emptyFirst
)

С учётом изменений, связанных с Option, теперь тесты должны покрывать все возможные варианты работы с пустыми значениями. Набор тестов для второй итерации доступен по ссылке.

Третья итерация

На текущем этапе проект позволяет создавать нетривиальные порядки сортировки для коллекций кошек. Тем не менее, существуют некоторые ограничения:

  1. Приоритет полей при сортировке заранее определён.
  2. Если для каких-либо полей порядок сортировки не задан, его всё ещё необходимо предоставить с помощью SortOrder.Keep.
  3. Количество полей класса Cat ограничено 9. После этого не существует метода Tuple10 и реализация должна существенно поменяться.

Новые бизнес-требования в этот раз относятся к последнему пункту. Теперь магазин предоставляет так много информации о кошках, что у класса Cat в наличии уже 10 полей. С учётом столь большого их числа, очевидно, что только лишь некоторые из них будут использоваться при сортировке. Более того, стало сложнее предопределить осмысленный приоритет полей, а явное определение SortOrder для каждого из них сделает код слишком многословным. В этот раз изменения будут более значительными.

Основная идея заключается в том, чтобы не только предоставить порядок сортировки определённых полей, но и сами поля. Набор полей и связанных с ними порядков сортировки затем трансформируется в порядок сортировки для Cat. Упорядоченная коллекция таких пар естественным образом задаёт необходимый приоритет сортировки (ограничение №1) и требуемые для сортировки поля (ограничение №2), а добавление в класс нового поля потребует лишь незначительных изменений и не повлияет на основную реализацию (ограничение №3). Реализация этой идеи приведена ниже (SortOrder и его синтаксическое расширение не приводятся ввиду отсутствия существенных изменений):

import java.time.LocalDate
case class Cat(age: Int,
               name: String,
               available: Boolean,
               owner: Option[String],
               breed: String,
               furColor: String,
               eyeColor: String,
               registrationId: String,
               lastHealthCheck: Option[LocalDate],
               urgentSell: Boolean)

import java.time.LocalDate
import iteration3.sort_order.SortOrder
import iteration3.sort_order.syntax._
import scala.Ordering._
sealed trait CatField {
  def toOrdering(sortOrder: SortOrder): Ordering[Cat]
}
object CatField {
  case object Age extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder(Ordering.Int).on(_.age)
  }
  case object Name extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder(Ordering.String).on(_.name)
  }
  case object Available extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder(Ordering.Boolean).on(_.available)
  }
  case object Owner extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder.optional(Ordering.String).on(_.owner)
  }
  case object Breed extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder(Ordering.String).on(_.breed)
  }
  case object FurColor extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder(Ordering.String).on(_.furColor)
  }
  case object EyeColor extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder(Ordering.String).on(_.eyeColor)
  }
  case object RegistrationId extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder(Ordering.String).on(_.registrationId)
  }
  case object LastHealthCheck extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder.optional(Ordering.by[LocalDate, Long](_.toEpochDay)).on(_.lastHealthCheck)
  }
  case object UrgentSell extends CatField {
    override def toOrdering(sortOrder: SortOrder): Ordering[Cat] =
      sortOrder(Ordering.Boolean).on(_.urgentSell)
  }
}

import common.OrderingUtil
import iteration3.sort_order.SortOrder
object CatOrdering {
  def byFields(fields: Seq[(CatField, SortOrder)]): Ordering[Cat] =
    if (fields.isEmpty) OrderingUtil.identity[Cat]
    else {
      val (head, headOrder) = fields.head
      val (res, _) = fields.tail.foldLeft[(Ordering[Cat], Set[CatField])]((head.toOrdering(headOrder), Set())) {
        case (acc@(_, presentFields), (field, _)) if presentFields.contains(field) =>
          acc
        case ((ordering, presentFields), (field, order)) =>
          (ordering.orElse(field.toOrdering(order)), presentFields + field)
      }
      res
    }
}

Реализация в основном полагается на метод orElse, применяющий другой порядок сортировки, если согласно уже имеющемуся порядку сравниваемые объекты эквивалентны. Он во многом идентичен методу thenComparing класса Comparator в языке Java. Данный метод доступен также и для Ordering, поскольку он является расширением Comparator в целях совместимости. Тем не менее, orElse гораздо лучше работает с классами языка Scala, а также обладает удобной альтернативой orElseBy.

Необходимо отметить, что, поскольку функция byFields применяется к произвольной упорядоченной коллекции без ограничений на уникальность её элементов, повторные вхождения обрабатываются вручную. Также, чтобы избежать излишних сравнений в OrderingUtil.identity, случай непустой коллекции обрабатывается отдельно. Данные особенности несут исключительно оптимизационный характер и не влекут функциональных отличий от реализации «в лоб», использующей foldLeft, поскольку повторные вхождения не влияют на результат.

Дополнительным преимуществом является возможность избавиться от SortOrder.Keep, поскольку коллекция, используемая для создания порядка сортировки, будет включать только необходимые поля и игнорировать остальные. Для порядка сортировки, не меняющего порядок элементов, необходимо предоставить пустую коллекцию. Это достаточно удобно при отображении в объекты предметной области HTTP запросов или пользовательских команд, которые будут предоставлять только требуемые правила сортировки.

Недостатком такого подхода является более многословная модель. Вероятно, подобного рода перечисление полей так или иначе понадобилось бы для вышеупомянутого отображения запросов, но определения полей выглядят скорее шаблонным кодом, чем полноценной частью логики. Четвёртая итерация могла бы определять макрос компилятора (с поддержкой аннотаций для нетривиальных сортировок) для автоматической генерации объектов полей, но представляется сомнительным, что реальные проекты дойдут до этого этапа. Текущий подход, тем не менее, позволяет легко добавлять и удалять поля, поскольку код, связанный с заданием порядка сортировки, никак не изменится.

Набор тестов для третьей итерации доступен по ссылке.

Заключение

Хотя итоговая реализация может выглядеть похожим образом и в других языках программирования, мне кажется, что Scala предоставляет хороший компромис между типобезопасностью и понятностью и читаемостью кода. Надеюсь, что данная публикация помогла лучше понять, как можно сортировать коллекции в Scala, или подсказала идеи, которые можно применить в коде, не связанном напрямую с Ordering.

Все примеры кода доступны в данном github репозитории

Теги:
Хабы:


Source: habr1

Метки:





Запись опубликована: 06.04.2020

Анализ международных документов по управлению рисками информационной безопасности. Часть 2

В предыдущей части мы описали общую концепцию риск-менеджмента и раскрыли методы управления рисками в соответствии с документами NIST SP серии 800. В данной части мы продолжим обзор международных документов по управлению рисками информационной безопасности: у нас на очереди стандарты ISO 27005 и 31010. Приступим!

image

Рассмотренные ранее специальные публикации NIST SP 800-39, NIST SP 800-37 и NIST SP 800-30 предлагают логически связанный системный подход к оценке и обработке рисков, а NIST SP 800-53, NIST SP 800-53A и NIST SP 800-137 предлагают конкретные меры по минимизации рисков ИБ. Однако следует иметь ввиду, что данные документы по своей сути носят лишь рекомендательный характер и не являются стандартами (например, в отличие от документов NIST FIPS), а также то, что изначально они разрабатывались для компаний и организаций из США. Это накладывает определенные ограничения на их использование: так, организации не могут получить международную сертификацию по выполнению положений данных документов, а применение всего набора связанных фреймворков NIST может оказаться чрезмерно трудозатратным и нецелесообразным. Зачастую компании выбирают путь сертификации по требованиям Международной Организации по Стандартизации (англ. International Organization for Standardization, ISO), получая, например, статус ”ISO 27001 Certified”, признаваемый во всем мире. В серию стандартов ISO 27000 входят документы, посвященные информационной безопасности и управлению рисками. Рассмотрим основной документ данной серии по управлению рисками ИБ: стандарт ISO/IEC 27005:2018.

ISO/IEC 27005:2018

Стандарт ISO/IEC 27005:2018 «Information technology — Security techniques — Information security risk management» («Информационные технологии – Техники обеспечения безопасности – Управление рисками информационной безопасности») является уже третьей ревизией: первая версия стандарта была опубликована в 2005 году, а вторая — в 2011. Документ вводит несколько риск-специфичных терминов. Так, средством защиты (англ. control) называется мера, изменяющая риск. В понятие контекстов (англ. context) входят внешний контекст, означающий внешнюю среду функционирования компании (например, политическую, экономическую, культурную среду, а также взаимоотношения с внешними стейкхолдерами), и внутренний контекст, означающий внутреннюю среду функционирования компании (внутренние процессы, политики, стандарты, системы, цели и культуру организации, взаимоотношения с внутренними стейкхолдерами, а также договорные обязательства).

Риск — это результат неточности (англ. uncertainty) при достижении целей; при этом неточность означает состояние недостатка информации, относящейся к некому событию, его последствиям или вероятности его наступления. Под уровнем риска (англ. level of risk) понимается величина риска, выраженная в произведении последствий значимых событий и вероятности возникновения этих событий. Остаточный риск (англ. residual risk) — риск, оставшийся после проведения процедуры обработки рисков. Под оценкой риска (англ. risk assessment) понимают общий процесс идентификации (т.е. поиска, определения и описания риска), анализа (т.е. понимания природы риска и определения его уровня) и оценки опасности (т.е. сравнения результатов анализа риска с риск-критериями для определения допустимости его величины) рисков. Обработка рисков — это процесс модификации рисков, который может включать в себя:

  • избегание риска путем отказа от действий, которые могут привести к рискам;
  • принятие или увеличение риска в целях достижения бизнес-целей;
  • устранение источников риска;
  • изменение вероятности реализации риска;
  • изменение ожидаемых последствий от реализации риска;
  • перенос (разделение) риска;
  • сохранение риска.

Процесс управления рисками ИБ с точки зрения авторов стандарта ISO/IEC 27005:2018 должен характеризоваться следующими особенностями:

  1. Оценка рисков ведется с учетом последствий рисков для бизнеса и вероятности возникновения рисков. Осуществляются идентификация рисков, их анализ и сравнение (с учетом выбранного уровня риск-толерантности).
  2. Вероятность и последствия рисков доводятся до заинтересованных сторон и принимаются ими.
  3. Устанавливается приоритет обработки рисков и конкретных действий по снижению рисков.
  4. В процесс принятия решений по управлению рисками вовлекаются стейкхолдеры, которые затем также информируются о статусе управления рисками.
  5. Оценивается эффективность проведенной обработки рисков.
  6. Контролируются и регулярно пересматриваются риски и сам процесс управления ими.
  7. На основе получаемой новой информации процесс управления рисками непрерывно улучшается.
  8. Проводится обучение сотрудников и руководителей относительно рисков и предпринимаемых действий для их снижения.

Сам процесс управления рисками состоит из следующих шагов (процессов), которые соответствуют принятому в стандарте ISO 27001 подходу PDCA (Plan — Do — Check — Act):

  1. Определение контекста.
  2. Оценка рисков.
  3. Разработка плана обработки рисков.
  4. Принятие рисков.
  5. Внедрение разработанного плана обработки рисков.
  6. Непрерывный мониторинг и пересмотр рисков.
  7. Поддержка и улучшение процесса управления рисками ИБ.

Рассмотрим далее каждый из этих шагов подробнее.

1. Определение контекста


Входными данными при определении контекста являются все релевантные риск-менеджменту сведения о компании. В рамках данного процесса выбирается подход к управлению рисками, который должен включать в себя критерии оценки рисков, критерии оценки негативного влияния (англ. impact), критерии принятия рисков. Кроме этого, следует оценить и выделить необходимые для осуществления данного процесса ресурсы.

Критерии оценки рисков должны быть выработаны для оценки рисков ИБ в компании и должны учитывать стоимость информационных активов, требования к их конфиденциальности, целостности, доступности, роль информационных бизнес-процессов, требования законодательства и договорных обязательств, ожидания стейкхолдеров, возможные негативные последствия для гудвилла и репутации компании.

Критерии оценки негативного влияния должны учитывать уровень ущерба или затрат компании на восстановление после реализованного риска ИБ с учетом уровня значимости ИТ актива, нарушения информационной безопасности (т.е. потери активом свойств конфиденциальности, целостности, доступности), вынужденный простой бизнес-процессов, экономические потери, нарушение планов и дедлайнов, ущерб репутации, нарушение требований законодательства и договорных обязательств.

Критерии принятия рисков можно выразить как отношение ожидаемой бизнес-выгоды к ожидаемому риску. При этом для разных классов рисков можно применять различные критерии: например, риски несоответствия законодательству могут не быть приняты в принципе, а высокие финансовые риски могут быть приняты, если они являются частью договорных обязательств. Кроме этого, следует учитывать и прогнозируемый временной период актуальности риска (долгосрочные и краткосрочные риски). Критерии принятия рисков необходимо разрабатывать, учитывая желаемый (целевой) уровень риска с возможностью принятия топ-менеджментом рисков выше этого уровня в определенных обстоятельствах, а также возможность принятия рисков при условии последующей обработки рисков в течение оговоренного временного периода.

Кроме вышеперечисленных критериев, в рамках процесса определения контекста следует учесть границы и объем (англ. scope) процесса управления рисками ИБ: нужно принять во внимание бизнес-цели, бизнес-процессы, планы и политики компании, структуру и функции организации, применимые законодательные и иные требования, информационные активы, ожидания стейкхолдеров, взаимодействие с контрагентами. Рассматривать процесс управления рисками можно в рамках конкретной ИТ-системы, инфраструктуры, бизнес-процесса или в рамках определенной части всей компании.

2. Оценка рисков


В рамках проведения процесса оценки рисков компания должна оценить стоимость информационных активов, идентифицировать актуальные угрозы и уязвимости, получить информацию о текущих средствах защиты и их эффективности, определить потенциальные последствия реализации рисков. В результате оценки рисков компания должна получить количественную или качественную оценку рисков, а также приоритизацию этих рисков с учетом критериев оценки опасности рисков и целей компании. Сам процесс оценки рисков состоит из действий по идентификации (англ. identification) рисков, анализа (англ. analysis) рисков, оценки опасности (англ. evaluation) рисков.

2.1. Идентификация рисков


Целью идентификации рисков является определение того, что может случиться и привести к потенциальному ущербу, а также получить понимание того, как, где и почему этот ущерб может произойти. При этом следует учитывать риски вне зависимости от того, находится ли источник этих рисков под контролем организации или нет. В рамках данного процесса следует провести:

  1. идентификацию (инвентаризацию) активов, получив в итоге список ИТ-активов и бизнес-процессов;
  2. идентификацию угроз, при этом следует учитывать преднамеренные и случайные угрозы, внешние и внутренние источники угроз, а информацию о возможных угрозах можно получать как у внутренних источников в организации (юристы, HR, IT и т.д.), так и у внешних (страховые компании, внешние консультанты, статистическая информация и т.д.);
  3. идентификацию имеющихся и запланированных к внедрению мер защиты для исключения их дублирования;
  4. идентификацию уязвимостей, которые могут быть проэксплуатированы актуальными угрозами и нанести ущерб активам; при этом следует учитывать уязвимости не только в программном или аппаратном обеспечении, но и в структуре организации, её бизнес-процессах, персонале, физической инфраструктуре, отношениях с контрагентами;
  5. идентификацию последствий реализации угроз нарушения конфиденциальности, целостности, доступности ИТ-активов.

2.2. Анализ рисков


Анализ рисков может быть проведен с различной глубиной, в зависимости от критичности активов, количества известных уязвимостей, а также с учетом ранее произошедших инцидентов. Методология анализа рисков может быть как качественной, так и количественной: как правило, вначале применяют качественный анализ для выделения высокоприоритетных рисков, а затем уже для выявленных рисков применяют количественный анализ, который является более трудоемким и дает более точные результаты.

При использовании качественного анализа специалисты оперируют шкалой описательной оценки опасности (например, низкая, средняя, высокая) потенциальных последствий неких событий и вероятности наступления этих последствий.

При использовании методов количественного анализа уже применяются численные величины, с учетом исторических данных об уже произошедших инцидентах. Следует при этом иметь ввиду, что в случае отсутствия надежных, проверяемых фактов количественная оценка рисков может дать лишь иллюзию точности.

При непосредственно самом процессе анализа рисков сначала проводится оценка потенциальных последствий инцидентов ИБ: оценивается уровень их негативного влияния на компанию с учетом последствий от нарушений свойств конфиденциальности, целостности, доступности информационных активов. Проводятся проверка и аудит имеющихся активов с целью их классификации в зависимости от критичности, также оценивается (желательно в денежных величинах) потенциальное негативное влияние нарушения свойств ИБ этих активов на бизнес. Оценка стоимости активов проводится в рамках анализа негативного влияния на бизнес (англ. Business Impact Analysis) и может быть рассчитана исходя из стоимости замены или восстановления активов/информации, а также последствий утери или компрометации активов/информации: рассматриваются финансовые, юридические, репутационные аспекты. Следует также учитывать, что угрозы могут затронуть один или несколько взаимосвязанных активов либо затронуть активы лишь частично.

Далее проводится оценка вероятности возникновения инцидента, т.е. всех потенциальных сценариев реализации угроз. Следует учесть частоту реализации угрозы и легкость эксплуатации уязвимостей, руководствуясь статистической информацией об аналогичных угрозах, а также данными о мотивации и возможностях преднамеренных источников угроз (построение модели нарушителя), привлекательности активов для атакующих, имеющихся уязвимостях, примененных мерах защиты, а в случае рассмотрения непреднамеренных угроз — учитывать местоположение, погодные условия, особенности оборудования, человеческие ошибки и т.д. В зависимости от требуемой точности оценки активы можно группировать или разделять с точки зрения применимых к ним сценариев атак.

Наконец, проводится определение уровня рисков для всех сценариев из разработанного списка сценариев атак. Величина ожидаемого риска является произведением вероятности сценария инцидента и его последствий.

2.3. Оценка опасности рисков


В рамках процесса оценки опасности рисков проводится сравнение полученных на предыдущем этапе уровней рисков с критериями сравнения рисков и критериями принятия рисков, полученными на этапе определения контекста. При принятии решений следует учитывать последствия реализации угроз, вероятность возникновения негативных последствий, уровень собственной уверенности в корректности проведенной идентификации и анализа рисков. Следует учесть свойства ИБ активов (например, если потеря конфиденциальности нерелевантна для организации, то все риски, нарушающие данное свойство, можно отбросить), а также важность бизнес-процессов, обслуживаемых определенным активом (например, риски, затрагивающие малозначимый бизнес-процесс, могут быть признаны низкоприоритетными).

3. Обработка рисков ИБ


К началу осуществления данного подпроцесса у нас уже имеется список приоритизированных рисков в соответствии с критериями оценки опасности рисков, связанных со сценариями инцидентов, которые могут привести к реализации этих рисков. В результате прохождения этапа обработки рисков мы должны выбрать меры защиты, предназначенные для модификации (англ. modification), сохранения (англ. retention), избегания (англ. avoidance) или передачи (англ. sharing) рисков, а также обработать остаточные риски и сформировать план обработки рисков.

Указанные опции обработки рисков (модификацию, сохранение, избегание или передачу) следует выбирать в зависимости от результатов процесса оценки рисков, ожидаемой оценки стоимости внедрения мер защиты и ожидаемых преимуществ каждой опции, при этом их можно комбинировать (например, модифицировать вероятность риска и передавать остаточный риск). Предпочтение следует отдавать легкореализуемым и низкобюджетным мерам, которые при этом дают большой эффект снижения рисков и закрывают большее количество угроз, а в случае необходимости применения дорогостоящих решений следует давать экономическое обоснование их применению. В целом, следует стремиться максимально снизить негативные последствия, а также учитывать редкие, но разрушительные риски.

В итоге ответственными лицами должен быть сформирован план обработки рисков, который чётко определяет приоритет и временной интервал, в соответствии с которыми следует реализовать способ обработки каждого риска. Приоритеты могут быть расставлены по результатам ранжирования рисков и анализа затрат и выгод (англ. cost-benefit analysis). В случае, если в организации уже были внедрены какие-либо меры защиты, будет разумно проанализировать их актуальность и стоимость владения, при этом следует учитывать взаимосвязи между мерами защиты и угрозами, для защиты от которых данные меры применялись.

В окончании составления плана обработки рисков следует определить остаточные риски. Для этого могут потребоваться обновление или повторное проведение оценки рисков с учетом ожидаемых эффектов от предлагаемых способов обработки рисков.

Далее рассмотрим подробнее возможные опции обработки рисков.

3.1. Модификация рисков


Модификация рисков подразумевает такое управление рисками путём применения или изменения мер защиты, которое приводит к оценке остаточного риска как приемлемого. При использовании опции модификации рисков выбираются оправданные и релевантные меры защиты, которые соответствуют требованиям, определенным на этапах оценки и обработки рисков. Следует учитывать разнообразные ограничения, такие как стоимость владения средствами защиты (с учетом внедрения, администрирования и влияния на инфраструктуру), временные и финансовые рамки, потребность в обслуживающем эти средства защиты персонале, требования по интеграции с текущими и новыми мерами защиты. Также нужно сравнивать стоимость указанных затрат со стоимостью защищаемого актива.К мерам защиты можно отнести: коррекцию, устранение, предотвращение, минимизацию негативного влияния, предупреждение потенциальных нарушителей, детектирование, восстановление, мониторинг и обеспечение осведомленности сотрудников.

Результатом шага «Модификация рисков» должен стать список возможных мер защиты с их стоимостью, предлагаемыми преимуществами и приоритетом внедрения.

3.2. Сохранение риска


Сохранение риска означает, что по результатам оценки опасности риска принято решение, что дальнейшие действия по его обработке не требуются, т.е. оценочный уровень ожидаемого риска соответствует критерию принятия риска. Отметим, что эта опция существенно отличается от порочной практики игнорирования риска, при которой уже идентифицированный и оцененный риск никак не обрабатывается, т.е. решение о его принятии официально не принимается, оставляя риск в «подвешенном» состоянии.

3.3. Избегание риска


При выборе данной опции принимается решение не вести определенную деятельность или изменить условия её ведения так, чтобы избежать риска, ассоциированного с данной деятельностью. Это решение может быть принято в случае высоких рисков или превышения стоимости внедрения мер защиты над ожидаемыми преимуществами. Например, компания может отказаться от предоставления пользователям определенных онлайн-услуг, касающихся персональных данных, исходя из результатов анализа возможных рисков утечки такой информации и стоимости внедрения адекватных мер защиты.

3.4. Передача риска


Риск можно передать той организации, которая сможет управлять им наиболее эффективно. Таким образом, на основании оценки рисков принимается решение о передаче определенных рисков другому лицу, например, путем страхования киберрисков (услуга, набирающая популярность в России, однако до сих пор в разы отстающая от объема этого рынка, например, в США) или путем передачи обязанности по мониторингу и реагированию на инциденты ИБ провайдеру услуг MSSP (Managed Security Service Provider) или MDR (Managed Detection and Response), т.е. в коммерческий SOC. При выборе опции передачи риска следует учесть, что и сама передача риска может являться риском, а также то, что можно переложить на другую компанию ответственность за управление риском, но нельзя переложить на нее ответственность за негативные последствия возможного инцидента.

4. Принятие риска


Входными данными этого этапа будут разработанные на предыдущем шаге планы обработки рисков и оценка остаточных рисков. Планы обработки рисков должны описывать то, как оцененные риски будут обработаны для достижения критериев принятия рисков. Ответственные лица анализируют и согласовывают предложенные планы обработки рисков и финальные остаточные риски, а также указывают все условия, при которых данное согласование выносится. В упрощенной модели проводится банальное сравнение величины остаточного риска с ранее определенным приемлемым уровнем. Однако следует учитывать, что в некоторых случаях может потребоваться пересмотр критериев принятия рисков, которые не учитывают новые обстоятельства или условия. В таком случае ответственные лица могут быть вынуждены принять такие риски, указав обоснование и комментарий к решению о невыполнении критериев принятия рисков в конкретном случае.

В итоге, формируется список принимаемых рисков с обоснованием к тем, которые не соответствуют ранее определенным критериям принятия рисков.

5. Внедрение разработанного плана обработки рисков. Коммуницирование рисков ИБ


На данном этапе осуществляется непосредственное претворение в жизнь разработанного плана обработки рисков: в соответствии с принятыми решениями закупаются и настраиваются средства защиты и оборудование, заключаются договоры кибер-страхования и реагирования на инциденты, ведется юридическая работа с контрагентами. Параллельно до руководства и стейкхолдеров доводится информация о выявленных рисках ИБ и принимаемых мерах по их обработке в целях достижения всеобщего понимания проводимой деятельности.
Разрабатываются планы коммуникации рисков ИБ для ведения скоординированной деятельности в обычных и экстренных ситуациях (например, на случай крупного инцидента ИБ).

6. Непрерывный мониторинг и пересмотр рисков


Следует учитывать, что риски могут незаметно меняться со временем: изменяются активы и их ценность, появляются новые угрозы и уязвимости, изменяются вероятность реализации угроз и уровень их негативного влияния. Следовательно, необходимо вести непрерывный мониторинг происходящих изменений, в том числе с привлечением внешних контрагентов, специализирующихся на анализе актуальных угроз ИБ. Требуется проводить регулярный пересмотр как рисков ИБ, так и применяемых способов их обработки на предмет актуальности и адекватности потенциально изменившейся ситуации. Особое внимание следует уделять данному процессу в моменты существенных изменений в работе компании и осуществляющихся бизнес-процессов (например, при слияниях/поглощениях, запусках новых сервисов, изменении структуры владения компанией и т.д.).

7. Поддержка и улучшение процесса управления рисками ИБ


Аналогично непрерывному мониторингу рисков следует постоянно поддерживать и улучшать сам процесс управления рисками для того, чтобы контекст, оценка и план обработки рисков оставались релевантными текущей ситуации и обстоятельствам. Все изменения и улучшения требуется согласовывать с заинтересованными сторонами. Критерии оценки и принятия рисков, оценка стоимости активов, имеющиеся ресурсы, активность конкурентов и изменения в законодательстве и контрактных обязательствах должны соответствовать актуальным бизнес-процессам и текущим целям компании. В случае необходимости нужно менять или совершенствовать текущий подход, методологию и инструменты управления рисками ИБ.

IEC 31010:2019


Рассмотрим теперь вкратце стандарт IEC 31010:2019 «Risk management — Risk assessment techniques» («Менеджмент риска — Методы оценки риска»).

Данный стандарт входит в серию стандартов по управлению бизнес-рисками без привязки конкретно к рискам ИБ. «Заглавным» стандартом является документ ISO 31000:2018 ”Risk management – Guidelines” («Менеджмент риска — Руководства»), который описывает фреймворк, принципы и сам процесс управления рисками. Описанный в данном документе процесс риск-менеджмента аналогичен рассмотренному выше: определяются контекст, границы и критерии, проводится оценка рисков (состоящая из идентификации, анализа, оценки опасности рисков), далее идет обработка рисков с последующей коммуникацией, отчетностью, мониторингом и пересмотром.

Стандарт же IEC 31010:2019 примечателен тем, что в нем приведено более 40-ка разнообразных техник оценки риска, к каждой дано пояснение, указан способ применения для всех подпроцессов оценки риска (идентификация риска, определение источников и причин риска, анализ мер защиты, анализ последствий, вероятностей, взаимосвязей и взаимодействий, измерение и оценка уровня риска, выбор мер защиты, отчетность), а для некоторых техник приведены и практические примеры использования. Кроме того, на данный стандарт в его отечественном варианте ГОСТ Р ИСО/МЭК 31010-2011 «Менеджмент риска. Методы оценки риска» ссылается 607-П ЦБ РФ «О требованиях к порядку обеспечения бесперебойности функционирования платежной системы, показателям бесперебойности функционирования платежной системы и методикам анализа рисков в платежной системе, включая профили рисков».

Теги:
Хабы:


Source: habr1

Метки:





Запись опубликована: 06.04.2020

Маркетинг в TikTok: подробное руководство

  • Более 500 шаблонов в галерее
  • Инструменты оптимизации конверсии
  • Статистика и сквозная аналитика
  • CRM для работы с заявками и телефония
  • Визуальный редактор с расширенным функционалом
  • Быстрая техническая поддержка
  • Множество интеграций
  • Окупаемость инструмента — от 7 дней

Несколько недель назад в газете The New York Times была опубликована статья под названием «TikTok меняет мир». В ней утверждается, что TikTok окажет сильное влияние на работу социальных сетей, даже если вы еще не пользуетесь им.

И с этим трудно поспорить.

TikTok определенно заслуживает вашего внимания, так как с 2019 года оно занимает третье место по количеству загрузок в Apple Store и Google Play — и его популярность продолжает расти! Сегодня им пользуется уже более 188 миллионов человек, а его месячная аудитория больше, чем у Twitter или Snapchat.

Что такое TikTok?

Немного истории

Аудитория TikTok

Как пользоваться TikTok

Почему TikTok так популярен?

1. Пользователи просто хотят повеселиться2. Рассылка уведомлений3. Всегда наготове4. Приложение для каждого

Уникальные особенности TikTok

1. Вертикальный формат2. Их видео — ваше видео3. Да будет музыка!4. Легко использовать чужую музыку5. Двойное веселье
6. WOW-факторы
7. Приложение знает, что вам понравится
8. Творчество каждый день
9. Вы всегда знаете, что сейчас в тренде

Нужен ли вам маркетинг в TikTok?

Реклама в TikTok

Виды рекламы в TikTok

1. Нативная реклама
2. Takeover Ads
3. «Хештег челлендж» реклама

Как продвигать свой бренд в TikTok

1. Пользовательский контент
2. Маркетинг влияния

Бренды, которые успешно продвигают себя в TikTok

Burberry
Hero Cosmetics
Chipotle
The Washington Post

Пора действовать!

Что такое TikTok?

TikTok — это креативная социальная платформа, которая позволяет людям загружать короткие видеоролики. Длительность большинства из них не более 15 секунд, но есть возможность делиться и 60-секундными видео. Что действительно делает приложение таким уникальным и популярным, так это возможность наложить на видеоролик музыкальный трек или разнообразные фильтры и эффекты.

Сотрудники TikTok уверены: ролик, созданный на их платформе, имеет гораздо больше шансов стать вирусным, чем в какой-либо другой социальной сети.

Также как в Instagram или Snapchat, видео для TikTok должны записываться вертикальном формате — так они занимают большую часть экрана смартфона, что положительно влияет на пользовательский опыт.

Немного истории

TikTok — это глобальная версия приложения для социальных сетей, выпущенного в Китае в 2016 году под названием Douyin. Эта платформа была создана популярной китайской IT-компанией ByteDance, которой совсем недавно было присвоено звание Единорога №1. Посмотрите, какую позицию она занимает в списке ведущих стартапов:

Ведущие стартапы, состояние которых оценивается в один миллиард долларов и выше (по данным на октябрь 2019)

Но TikTok назывался так не всегда. Первоначально он был создан в 2014 году под именем Musical.ly. Через приложение пользователи могли создавать видеоролики продолжительностью от 15 секунд до 1 минуты и выбирать к ним сопровождающие звуковые дорожки, использовать разные параметры скорости (замедленные, медленные, нормальные, быстрые и «эпические») и добавлять предварительно установленные фильтры и эффекты. А затем, в 2018 году ByteDance приобрела Musical.ly, объединив его с собственным приложением Douyin.

Уже к сентябрю 2018 TikTok перегнал Facebook, Instagram, YouTube и Snapchat по количеству скачиваний. И, скорее всего, стремительный рост популярности этого приложения будет продолжаться.

Аудитория TikTok

Фактически, 41% пользователей платформы — это люди в возрасте от 16 до 24 лет. Интересно то, что когда приложение было запущено, более 50% ее пользователей были моложе 24 лет. И эти первые пользователи, похоже, продолжили пользоваться платформой. Это означает, что средний возраст пользователей приложения постепенно увеличивается. И хотя TikTok все еще пользуется огромной популярностью среди молодых людей, объем взрослой аудитории также продолжает расти.

Количество взрослых пользователей TikTok (октябрь 2017 — март 2019, данные по США)

Стоит отметить, что ничто в мире социальных сетей не является чистой удачей. Наоборот, успех социальных медиа зависит исключительно от того, насколько заманчивыми и привлекательными они кажутся пользователям.

Кроме того, не стоит думать, что TikTok так популярен только в США. В настоящее время приложение насчитывает около 500 миллионов активных пользователей по всему миру, причем 150 из них находятся в Китае. TikTok также очень популярен в азиатских странах, таких как Камбоджа, Япония, Индонезия, Малайзия, Таиланд и Вьетнам. Сегодня TikTok доступен в 155 странах и на 75 языках.

Что касается нашей страны, то в России месячная активная аудитория TikTok в январе 2020 года достигла 18 миллионов пользователей. Об этом сообщил представитель TikTok, выступивший на конференции «Маркетинг #реальноговремени». По данным компании, на сегодня среди пользователей TikTok преобладают женщины: их 60%, мужчин — 40%. Дети и подростки составляют 43% аудитории, люди в возрасте от 18 до 34 лет — 33%, от 25 до 34 лет — 21%.

Как пользоваться TikTok

TikTok очень легок в использовании независимо от того, хотите ли вы просто убить время или опубликовать собственное видео.

Процесс входа в систему тоже довольно прост. Учетную запись можно создать либо по номеру телефона, либо через аккаунт Facebook, Twitter или Gmail.

Как только вы ее создадите, вам не придется никого фолловить и ни на кого подписываться, чтобы начать смотреть видео. Они просто появляются на вашем экране,а чтобы посмотреть следующий ролик, вам достаточно провести пальцем вверх.

Еще одна замечательная особенность, касающаяся TikTok, это то, что все нужные кнопки расположены на одном экране. Так что вы можете добавлять, отслеживать, делиться и комментировать видео прямо во время воспроизведения.

Для создания вашего первого рекламного видеоролика TikTok, нужно просто нажать значок «+» в нижней части экрана. Выберите саундтрек к своему видео, нажав значок в верхней части экрана. Вам откроется целая библиотека песен и звуковых эффектов.

После съемки видео вы также можете добавлять всевозможные эффекты, наклейки и смайлики, чтобы сделать его еще более уникальным. Когда вы закончите, нужно нажать на розовый флажок, чтобы перейти на страницу предварительного просмотра и редактирования. Нужно нажать «Дальше», и ваше видео станет доступным всему миру.

Почему TikTok так популярен?

В первом квартале 2019 TikTok стал первым приложением в Apple App Store по количеству скачиваний. Его загрузили 33 миллиона раз.

TikTok — первое приложение по количеству скачиваний в Apple App Store.

Приложение сильно превосходит YouTube, Instagram, WhatsApp и Facebook Messenger. И вот в чем причины популярности TikTok:

1. Пользователи просто хотят повеселиться

TikTok не случайно завоевал такую популярность именно среди молодых людей. Он был создан специально для них.

Создатели приложения решили выбрать в качестве целевой аудитории людей до 18 лет. Поэтому перед запуском они выяснили все особенности и предпочтения этой возрастной группы.

TikTok позволяет пользователям выразить себя современным, креативным способом, будь то пение, танцы или юмор. Хорошая новость для маркетологов заключается в том, что развлекаться хочется не только подросткам, поэтому аудитория TikTok постоянно расширяется, захватывая и другие возрастные группы, а само приложение становится отличной платформой для рекламы вашего бренда.

2. Рассылка уведомлений

Создатели всех платформ социальных сетей постоянно задаются одним и тем же вопросом: «Как привлечь больше пользователей?» TikTok нашли ответ: ключ к успеху кроется в рассылке уведомлений.

TikTok буквально атакует пользователей своими уведомлениями и напоминаниями. В итоге, вас чуть ли не за руку «приводят» на платформу. Бесспорно, такая настойчивость может раздражать, но этот прием реально работает.

3. Всегда наготове

Еще одна причина, по которой TikTok смог завоевать такую популярность, заключается в том, что он не играет по тем же правилам, что Facebook или Instagram. Как уже было упомянуто ранее, вам не нужно подписываться на других пользователей, чтобы иметь возможность просматривать контент. Вы сразу получаете рекомендации по контенту, которые, конечно, со временем совершенствуются, благодаря современным технологиям.

4. Приложение для каждого

Instagram заполнен фотографиями и историями идеальных людей с идеальной внешностью. У популярных Instagram-блогеров всегда идеальный макияж, и они ведут съемки в роскошных отелях или ресторанах.

А TikTok ориентирован на простых пользователей. 

Приложение поощряет видео всех видов и призывает пользователей создавать свой собственный контент. На TikTok вы увидите подростков, 30-летних, вашу маму, своего босса и даже своего дедушку. И все они могут завоевать свои 5 минут славы.

Уникальные особенности TikTok

TikTok может заставить вас «застрять» в нем надолго. Все это благодаря уникальным возможностям приложения.

Многочисленные опции дают пользователям возможность создавать уникальный и интересный контент. При этом, от них не требуют никаких специальных навыков.

Ниже будут перечислены основные особенности видео в TikTok.

1. Вертикальный формат

Люди чаще всего держат свои телефоны вертикально. Поэтому неудивительно, что вертикальное видео популярнее остальных. И хотя другие социальные сети все еще верят, что могут заставить пользователей смотреть видео в горизонтальном формате, TikTok улучшает впечатления от просмотра.

Использование вертикального формата также дает важную психологическую выгоду. Так как нет необходимости переворачивать свои телефоны, пользователям легче взаимодействовать с автором видео и оставлять комментарии.

2. Их видео — ваше видео

Если говорить о вовлеченности, то TikTok и здесь превзошел другие соцсети. Здесь вы можете не только просматривать и лайкать понравившиеся ролики, но и скачивать их на свой смартфон.

3. Да будет музыка!

Ни одно из приложений для социальных сетей не дает возможность наложить музыку на свой видеоролик. Эта функция выделяет TikTok на фоне остальных. Приложение предлагает пользователям широкий выбор музыки и звуковых эффектов, которые позволят любому проявить свои творческие способности.

4. Легко использовать чужую музыку

Если, посмотрев видео, вам понравится музыка, которая наложена на ролик, то вы легко можете добавить ее себе. Всего несколько нажатий, и понравившаяся вам песня из чужого ролика станет вашей.

5. Двойное веселье

При помощи TikTok вы можете создавать совместные видео с другими пользователями.

Эта популярная функция называется «Дуэт». Вы можете разделить экран с другом, незнакомым пользователем или даже знаменитостью и создать совместное видео.

У многих приложений сегодня есть собственный набор фильтров и специальных эффектов для видео. Однако и здесь TikTok превзошел всех. Это приложение может похвастаться самым простым и интересным видеоредактором.

7. Приложение знает, что вам понравится

В этом кроется один из секретов успеха TikTok. Благодаря уникальным алгоритмам приложение точно знает, какие ролики вам предложить, чтобы они вам понравились. Список воспроизведения создается на основе поведения пользователя в интернете, а также включает в себя наиболее популярные и вирусные видео.

8. Творчество каждый день

Челленджи — это еще одна отличительная особенность TikTok. Они позволяют поддерживать вовлеченность аудитории на высоком уровне. Челлендж — жанр интернет-роликов, в которых блогер выполняет задание на видеокамеру и размещает его в сети, а затем предлагает повторить это задание своему знакомому или неограниченному кругу пользователей.

Таким образом, вам не нужно ломать голову над тем, о чем снять свой следующий ролик. Популярные челленджи #dancingclothes и #haribochallenge уже собрали более 43 миллионов просмотров.

Кроме того, пользователи сами создают челленджи, называя их собственными уникальными именами. В этот процесс включаются и бренды.

9. Вы всегда знаете, что сейчас в тренде

Хештеги являются неотъемлемой частью функционирования приложения. При этом вам не нужно прилагать никаких усилий, чтобы узнать, что сейчас в тренде. Достаточно открыть страницу Discover. Там вы найдете список видеороликов, рядом с которыми будет указано количество просмотров, поэтому вам легко будет оценить популярность того или иного контента.

Нужен ли вам маркетинг в TikTok?

Решение о том, подходит ли вам маркетинг в TikTok, во многом зависит от отрасли вашей компании и вашей целевой аудитории. Если вы хотите охватить молодежь и поколение Z, тогда эта социальная сеть может оказаться чрезвычайно полезной для вас, поскольку ее используют пока лишь немногие бренды, и, следовательно, у вас будет больше шансов быть замеченными.

Кроме того, TikTok является источником самых вирусных видео на данный момент, а такие ролики, в свою очередь, одна из лучших контент-стратегий на сегодняшний день.

Реклама в TikTok

Так как популярность TikTok стремительно возрастала, в феврале 2019 года владельцы TikTok объявили о создании платформы для рекламодателей. Она получила название TikTok Ads. Поэтому если вы всерьез намерены продвигать свой бренд в этой социальной сети, то сначала нужно зарегистрироваться на TikTok Ads: 

Весь процесс регистрации очень прост — закончив ее, вы получите уведомление, что компания свяжется с вами в течение 5 рабочих дней.

После завершения регистрации вам станет доступна ​​панель рекламодателя, на которой вы сможете создавать новые объявления и отслеживать существующие.

При создании новой рекламной кампании вы сможете настроить следующие параметры:

  • Позицию и таргетинг объявления
  • Рекламный бюджет
  • График показа объявлений
  • Частота показа объявлений
  • Цель кампании

Самое замечательно то, что TikTok помогает пользователям запускать свои кампании с помощью встроенного в приложение инструмента TikTok Video Creation Kit. Он предоставляет шаблоны видео и изображений для настройки ваших объявлений. Он также предоставляет более 300 бесплатных шаблонов фоновой музыки, чтобы сделать ваши кампании более уникальными.

Виды рекламы в TikTok

Существует три типа рекламы в TikTok:

1. Нативная реклама

Если вам интересны сторис в Instagram, то нативная реклама — это то, что вам нужно! Это могут быть видеообъявления продолжительностью от 5 до 15 секунд, которые отображаются на странице «Для вас» и имеют несколько вариантов оформления. Пользователь может по желанию скрыть рекламный видеоролик.

Прямо на видео можно добавлять ссылки на лендинги и призывы к действиям, например кнопки «Заказать» или «Скачать приложение».

2. Takeover Ads

Takeover Ads — это полноэкранные объявления, которые отображаются, когда пользователь впервые открывает TikTok.

Они представляют собой изображения или GIF-файлы, которые находятся на экране пользователя от 3 до 5 секунд. Их также можно привязать к посадочной странице вашего бренда или к Hashtag Challenge в приложении.

3. «Хештег челлендж» реклама

В этом типе рекламы пользователь видит баннер, который перенесет их на страницу с инструкциями и правилами конкурса. Баннер размещается на странице поиска и зависит от пользовательского контента. 

Хотя как владелец бренда вы можете предложить свой собственный челлендж, лучше всего спонсировать челленджи TikTok, чтобы получить больше шансов на вирусный эффект. Это означает, что ваш челлендж будет размещен на странице поиска шесть дней, что обеспечит вам максимальный охват и участие.

Как продвигать свой бренд в TikTok

— не единственный способ продвижения вашего бренда в TikTok. Вы также можете выбрать один из методов ниже:

1. Пользовательский контент

Пользовательский контент (UGC) всегда был огромным ресурсом для контент-маркетологов. А сейчас он даже эффективнее, чем когда-либо. В действительности, 85% потребителей считают сгенерированный пользователями контент более эффективным инструментом, чем брендированные фотографии или видео.

2. Маркетинг влияния

Маркетинг влияния — другой очень эффективный инструмент, особенно в TikTok, так как у платформы уже есть довольно много популярных пользователей с большой аудиторией.

Прежде чем выбрать агента влияния, с которым вы хотите сотрудничать, проведите небольшое исследование и задайте себе следующие вопросы:

  • Соответствует ли размещаемый на странице инфлюенсера контент вашему бренду?
  • Много ли у него поклонников?
  • Являются ли его поклонники вашей целевой аудиторией?

Бренды, которые успешно продвигают себя в TikTok

Крупный дом моды Burberry решил запустить челлендж #TBChallenge, чтобы прорекламировать свою новую коллекцию.

Компания пригласила пользователей воссоздать новую монограмму Thomas Burberry своими руками. 

По данным Business of Fashion, в результате челленджа было загружено 30 000 видео, которые в сумме набрали около 57 миллионов просмотров.

Hero Cosmetics

Hero Cosmetics — это бренд, выпускающий косметику для проблемной кожи. Они запустили рекламную кампанию в TikTok, результаты которой превзошли аналогичную кампанию в Instagram.

В рамках своей кампании «Готовься со мной» они создали хэштег #schoolsurvivalkit, где 20 пользователей поделились видео о своей утренней процедуре по уходу за кожей с использованием продукта бренда Mighty Patch.

Маркетологи отметили, что эта кампания была намного более рентабельной, чем аналогичная в Instagram Stories. Кроме того, коэффициент вовлеченности составил 12% по сравнению с 4,5% в Instagram.

Этот бренд проводит самые эффективные кампании в TikTok. Например, чтобы отпраздновать День Авокадо, они запустили челлендж #GuacDance, который призвал любителей авокадо придумать танец, посвященный этому фрукту.

Чтобы продвигать свой челлендж, бренд Chipotle сотрудничал с ютуберами Brent Rivera и Loren Grey. Маркетологи сообщили о рекордных результатах: 250 000 заявок за 6 дней.

The Washington Post

Американская ежедневная газета запустила свой аккаунт в мае, и с тех пор он стал одним из популярнейших в TikTok. Его цель — завоевать доверие юных зрителей приложения и познакомить их со статьями, которые публикуются в газете.

Они публикуют забавные, увлекательные видео, которые на сегодняшний день собрали более 309 тысяч подписчиков, в то время как Дейв Йоргесон, управляющий TikTok из Washington Post, говорит, что надеется в конечном итоге использовать платформу для распространения видео, которые будут одновременно информировать и развлекать.

Пора действовать!

Безусловно, TikTok — все еще новичок в мире социальных сетей, и многие крупные бренды еще даже не думали использовать его для продвижения своих товаров и услуг.

Но трудно отрицать тот факт, что популярность приложения растет. Очевидно, в нем есть что-то очень крутое и современное. А новые тенденции появляются на рынке очень быстро и в скором времени превращаются в эффективные маркетинговые инструменты. Изучение того, как можно использовать TikTok для вашего бизнеса, может стать толчком для стремительного роста.

Надеемся, эта статья вдохновила вас как минимум на создание профиля в TikTok и изучение возможностей этого приложения. Если же вы и так являетесь активным пользователем и решили завести аккаунт еще и в TikTok Ads, не забудьте создать рекламный лендинг и придумайте креативные и смешные призывы к действию под стать этой социальной платформе.

Высоких вам конверсий! 

По материалам: moosend.com

Иллюстрация к статье: 9 SMM-стратегий для малого бизнеса
Иллюстрация к статье: Новые SMM-хаки, которые можно опробовать уже сегодня
Иллюстрация к статье: Как небольшому SMM-агентству удалось увеличить конверсию своего лендинга более чем в 3 раза, используя инструменты сегментации трафика
Иллюстрация к статье: 50 фактов из статистики видеомаркетинга для эффективной SMM-рекламы в 2017 году
Иллюстрация к статье: MINI Cooper S: великая SMM-стратегия маленького автомобиля
Иллюстрация к статье: 7 SMM мифов, в которые все верят

Source: LpGenerator

Метки: