• Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.8:run что это такое?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Это значит, что в pom-е, который вы пытаетесь билдить, прописан (среди прочего) maven-antrun плагин, который что-то должен делать... но когда до этого доходит, он не может это "что-то" сделать, и билд падает. Почему это происходит, можно сказать только зная, что он конкретно пытается сделать... чаще всего, проблемы возникают от несоответствия окружения тому, под которое был настроен плагин: пути, права, отсутствие/наличие каких-нибудь файлов или даже Win/Linux. Все зависит от кривизны рук того, кто конфигурировал билд... или же того, кто пытается его выпонить, не прочитав инструкцию :)
    Ответ написан
    Комментировать
  • Цикл с таймаутом ровно в 1 секунду?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Ваш вопрос можно приводить в качестве наглядного пособия на тему "Как именно вылазит боком кривая архитектура системы" :) Основная проблема в том, что интеграция компонент через storage есть зло, рано или поздно (чаще - рано) заставляющее разработчика выполнять стойку на ушах. Так что, если есть такая возможность, постарайтесь устранить зло в корне, т.е. найти способ узнавать об изменениях, ну, или хотя бы о самом факте, до того, а не после. Тогда не придется ничего опрашивать в цикле, а только реагировать на изменения. А это уже - половина проблемы!

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

    "Примерно 0.03с" само по себе еще не о чем не говорит. Это всегда или в случае, если изменений нет, или если они небольшого объема? А если 90% данных обновились? Это - раз. Два: если изменения обнаружены, сколько может занять их обработка (в худшем случае)?.. И, наконец, три: если уже наступила "следующая секунда", а мы все еще не закончили обработку прошлых изменений, возникает целый ряд вопросов. Имеет ли в этом случае вообще смысл проверять новые изменения (сможем ли мы их осмысленно обработать, если найдем)? Если да, понадобится как минимум 2 потока. Если нет, насколько критично пропустить эту секунду? А сколько еще можно пропустить без ущерба для функционала? Предположим, это не критично, и мы уже пропустили секунду (или несколько), что нам важнее: чтоб следующая проверка выполнилась как можно ближе к границе "следующей секунды", или как можно быстрее? И т.д. и т.п.

    Не зная ответов на эти и подобные вопросы, невозможно предложить "правильное" решение. Но в качестве сферического коня в вакууме можно посоветовать бесконечный цикл, в котором выполняется проверка, реакция на найденные изменения, после чего вычисляется время до следующей проверки, на которое выполняется sleep().
    Ответ написан
    Комментировать
  • Суть виртуальной машины Java?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Тут дали уже много хороших и правильных ответов, но хотелось бы уточнить, что вот эта метафора:
    Виртуальня машина java это тоже интерпретатор по сути

    может направить по весьма ложному пути!

    У слов в названиях есть достаточно точный смысл, и JVM называется именно машиной, а не интерпретатором, и не компилятором совершенно не случайно. Компилятор в Яве есть (javac), и он нужен не для выполнения программы, а именно для ее компиляции (в байткод). Имено поэтому он не входит в состав JRE (среды выполнения), а содержится в JDK (среде разработки). В самой JVM есть еще один, JIT-компилятор, который компилирует байткод в инструкции процессора во время выполнения программы, но это уже другая история, и его тоже никак не назвать интерпретатором.

    По сути JVM - это процессор, только виртуальный. И как у любого процессора (железного, типа x86, или виртуального, типа CLR в .NET), у него есть свой набор опкодов, называемый байткодом. Так же точно, как на х86 может выполняться код, порожденный компилятором с C++, или Pascal, или Go, так же и на JVM может выполняться байткод, скомпилированный из Java, или Scala, или Kotlin (или даже написанный вручную), а .class -файл, это, по сути, тот же .exe (точнее .so), скомпилированный под "процессор JVM". В этом и заключается кроссплатформенность. Так же, как код, скомпилированный под х86 будет выполняться на процессоре от Intel или AMD, так же и байткод JVM будет выполняться на JVM от Oracle, IBM, OpenJDK и т.д. И даже наличие JIT, компилирующего байткод в опкод конкретного железного процессора во время выполнения, все еще не дает повода обзывать честную стековую (SUN) или регистровую (Dalvik) VM интерпретатором, пусть даже и по сути :)

    Дело в том, что сама эта классификация (интерпретируемый/компилируемый ЯЗЫК) последние лет эдак 25 уже практически лишена смысла. Языкам, изначально ориентированным на реализацию в виде интерпретатора (с просто анализируемой лексикой, чтоб интерпретатор был поменьше и мог оставить самой программе достаточно места в ограниченной по объему памяти) типа APL или BASIC, сейчас (кроме, разумеется, очень узкоспециального применения) почетное место разве что в старых учебниках, из которых эту самую классификацию, с достойным лучшего применения упорством, продолжают дословно переписывать в новые. При этом, почему-то, забывают уточнить, что эти два понятия уже давно не про сами языки, а всего лишь про некоторые методы их реализации, и что с тех пор помимо этих методов появилось еще много других хороших и разных концепций на эту тему (типа VM, JIT, сборщиков мусора, да и хотя бы тех же OOП, разных видов типизации и еще миллион чего), которых в тех учебниках еще просто не было в силу их года издания. И что на сегодня уже даже для языков, принципиально заточеных для компиляции под регистровую архитектуру, типа С, есть пруд пруди интерпретаторов (раз, два, три)... которые, опять же, никто не называет виртуальными машинами, т.к. это все суть разные понятия. Короче, это все равно что пытаться понять, где в квантовой механике огонь, вода, земля и воздух, в том виде, как их понимали Платон и Аристотель :)

    P.S. Чтоб осознать, когда эта классификация еще была актуальна, рекомендую вот это . Там создатели APL, одного из первых настоящих интерпретируемых языков, обсуждают насущные проблемы языкостроения того времени. Если туго с английским, посмотрите хотя бы вступление... в тех железяках было меньше памяти и вычислительной мощности, чем в современной симке :)
    Ответ написан
    Комментировать
  • Чем отличается микросервисная архитектура от сервис-ориентированной?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    В основном, очередностью "выхода в массы". Исторически, понятие SOA постулировали применительно к решению задач в области информационных систем масштаба предприятия. Потом стало очевидно, что те же самые принципы иногда весьма полезны и при проектировании архитектуры отдельных приложений. Ирония состоит в том, что основные идеи SOA тоже не с неба свалились, а были унаследованы с "микро уровня" (те же I2C и пр., придуманные задолго до того). Короче, говорят, что MS, это просто правильно сделанная SOA.

    Ну, а по сути, оба варианта говорят об одном и том же, только с разных сторон. SOA предлагает принципы интегрирования готовых частей в систему, а MS учит, как проектировать эти части, чтоб они лучше интегрировались в SOA... И все это, разумеется, с т.з. нефункциональных требований, как то масштабируемость, отказоустойчивость, сопровождаемость и далее по списку из ISO/IEC 25010.

    Почитать, например:
    martinfowler.com/articles/microservices.html
    amzn.com/1491950358
    Ответ написан
    Комментировать
  • Почему GROUP BY работает?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Есть разница между "работает" и "выдаёт детерминированный результат". В данном примере ни разу не очевидно, какой именно результат можно ожидать в полях, которые не агрегированы и по которым не происходит группировки. Кто-то решит втулить туда значение из первой записи, попавшей в группировку, кто-то - из последней, кто-то вообще решит включать в результат только группы с идентичным значением этого поля... короче, вариантов море, и все неочевидны, и не определены стандартом. В нормально спроектированных системах в таких случаях принято выбирать наиболее очевидный вариант, т.е. выдавать ошибку. Можно, конечно, поступать иначе, но полагаться на такую систему - было бы как минимум стремно.
    Ответ написан
    Комментировать
  • Inversion of Control vs Dependency Inversion?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Эти принципы просто относятся к разным вещам. IoC предлагает некий способ организации управления потоком выполнения в системе (кто кого почему и когда вызывает), в то время как DIP предлагает соблюдать определенное направление при организации зависимостей (между модулями), основываясь на уровне абстрактности оных.
    В общем оба они направлены на повышение качественных характеристик системы (и это в них, как и во всех прочих принципах ООД, действительно общее), только один "подходит к проблеме" со стороны поведенческих аспектов системы, а второй - со стороны структурных.
    Ответ написан
    2 комментария
  • Создание экземпляра класса эквивалентен созданию переменной?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Короче, в дополнение к уже имеющимся ответам и для их систематизации... Начнем с простых вещей.

    Есть принципиальная разница между объявлением переменной и присвоением ей значения. Для примитивных типов это не должно вызывать затруднений
    int x; // <---- declaration *
    ...
    x = 15; // <----- assignment
    ...
    int y = 25; // <------- declaration, then assignment

    * - тот факт, что в этом месте после объявления в переменной в большинстве имплементаций Явы будет значение 0, оставим на потом, и разберемся, в чем конкретно разница. Объявление приводит к тому, что где-то в памяти резервируется место, которое потом может быть (но может и не быть!) заполнено значением соответствующей длины (типа). Быть или не быть, полностью зависит от дальнейшей программы. А присвоение приводит к тому, что некое значение помещается в ранее объявленную переменную.
    (В этом суть! Всё остальное, включая неявное объявление переменных, анонимные классы, оптимизации компилятора и т.д. - сахар, для удобства и компактности/читаемости кода.)

    Далее. Откуда берется значение, которое присваивается переменной? Оно либо уже известно в момент компиляции кода (константа/литерал, как, например, 15 или 25 выше), или станет известно во время его выполнения. Второе имеет место быть в случае, когда тип переменной - ссылка на объект (экземпляр класса). В момент компиляции никакого экземпляра еще нет. Чтоб он возник (и стала известна ссылка на него, которую туда можно поместить), нужно еще выполнить много разных действий, первое из которых - попытаться выделить под него память (что, кстати говоря, может и не получиться, если ее по какой-либо причине в этот момент нет). Именно это и делает new - выделяет память под экземпляр класса, создает его, выполняет статическую инициализацию, вызывает подходящий конструктор (он может быть перегружен или наследоваться), и только если все прошло успешно - присваивает переменной ссылку на свеженький экземпляр.

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

    Ну и, наконец, отличить примитивный тип от объекта в Java очень просто :) По договоренности названия примитивных типов начинаются с маленькой буквы, а названия классов - с большой.
    Ответ написан
    Комментировать
  • Визуальное программирование для Джавы?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Существует, но не для Явы. Например, LabView. Там графический язык, на котором можно "написать" (нарисовать) программу, и увидеть её логику. А Ява - язык императивный, и визуализировать его, все равно, что пытаться заставить парусник ездить по железной дороге... теоретически - возможно, практически - довольно бессмысленно. Но если все равно очень хочется, то да... UML в помошь .
    Ответ написан
    Комментировать
  • Как реализовать симуляцию JAVA используя многопоточность?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    По заявкам телезрителей :)
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.Random;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    
    
    public class Simulation {
    
    	private static final Random globalRandom = new Random(System.currentTimeMillis());
    	
    	public synchronized int getRandomIntInRangeEnclosed(int min, int max){
    		return globalRandom.nextInt((max - min + 1) + min);
    	}
    	
    	private final static int CUSTOMER_NUMBER = 3000;
    	private final static int DESK_NUMBER = 6;
    
    	public final int ITEM_DURATION_S_MIN = 3;
    	public final int ITEM_DURATION_S_MAX = 8;
    
    	public final CustomerFactory customerFactory = new CustomerFactory(this, CUSTOMER_NUMBER);
    
    	public final ArrayList<DeskReport> results = new ArrayList<DeskReport>(DESK_NUMBER);
    	
    	public void perform(){
    		ExecutorService executor = Executors.newFixedThreadPool(DESK_NUMBER);
    		for (int i = 1; i <= DESK_NUMBER; i++) {
    			executor.submit(new Desk(this, i));
    		}
    
    		executor.shutdown();
            try {
                executor.awaitTermination(20, TimeUnit.SECONDS);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
    	}
    	
    	public void printResult(){
    		int maxTimeS = 0;
    		int maxItems = 0;
    		int maxCustomers = 0;
    		
    		int minItems = Integer.MAX_VALUE;
    		int minCustomers = Integer.MAX_VALUE;
    		
    		for(DeskReport r : results){
    			if(r.getSpentTime() > maxTimeS){
    				maxTimeS = r.getSpentTime();
    			}
    			
    			if(r.getSoldItemCount() > maxItems){
    				maxItems = r.getSoldItemCount();
    			}
    			if(r.getSoldItemCount() < minItems){
    				minItems = r.getSoldItemCount();
    			}
    			
    			
    			if(r.getServedCustomerCount() > maxCustomers){
    				maxCustomers = r.getServedCustomerCount();
    			}
    			if(r.getServedCustomerCount() < minCustomers){
    				minCustomers = r.getServedCustomerCount();
    			}
    			
    		}
    		
    		Date date = new Date(maxTimeS * 1000L);
    		DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
    		String dateFormatted = formatter.format(date);
    		
    		
    		System.out.println("---------------- RESULTS ----------------");
    		System.out.println(DESK_NUMBER + " desks served " + CUSTOMER_NUMBER + " customers in " + dateFormatted);
    		System.out.println("Troughput min ~ max:");
    		System.out.println(minItems + "\t~\t" + maxItems + " \titems/desk");
    		System.out.println(minCustomers + "\t~\t" + maxCustomers + "\tcustomers/desk");
    	}
    	
    	public static void main(String[] args) {
    		Simulation sim = new Simulation();
    		sim.perform();
    		sim.printResult();
    	}
    }
    public class CustomerFactory {
    
    	private Simulation simulation;
    	private int capacity = 0;
    	
    	public CustomerFactory(Simulation simulation, int capacity){
    		this.simulation = simulation;
    		this.capacity = capacity;
    	}
    
    	public synchronized Customer getInstanceWithRandomCapacity(){
    		if(capacity-- > 1){
    			return new Customer(this.simulation.getRandomIntInRangeEnclosed(1,20));
    		} else {
    			return null;
    		}
    	}
    	
    }
    
    public class Desk implements Runnable{
    	private Simulation s = null;
    	private int deskNo = 0;
    
    	private int servedCustomerCount = 0;
    	private int soldItemCount = 0;
    	private int spentTime = 0;
    	
    	public Desk(Simulation s, int deskNo) {
    		this.s = s;
    		this.deskNo = deskNo;
    	}
    	
    	public void run() {
    		System.out.println("Desk " + deskNo + " opening...");
    
    		Customer c = null;
    		while ((c = s.customerFactory.getInstanceWithRandomCapacity()) != null) {
    			servedCustomerCount++;
    			for(int i=c.getItemCount(); i>1; i--){
    				soldItemCount++;
    				spentTime += s.getRandomIntInRangeEnclosed(s.ITEM_DURATION_S_MIN, s.ITEM_DURATION_S_MAX);
    			}
    			//get short break after each customer ;)
    			sleep(Long.valueOf(s.getRandomIntInRangeEnclosed(s.ITEM_DURATION_S_MIN, s.ITEM_DURATION_S_MAX)));
    		}
    		
    		s.results.add(new DeskReport(deskNo, servedCustomerCount, soldItemCount, spentTime));
    		System.out.println("Desk " + deskNo + "\tclosing.\tcustomers " + servedCustomerCount + "\titems " + soldItemCount + "\ttime(s) " + spentTime);
    	}
    	
    	private void sleep(long ms){
    		try {
    			Thread.sleep(ms);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    public class DeskReport {
    	private int deskNo = 0;
    	private int servedCustomerCount = 0;
    	private int soldItemCount = 0;
    	private int spentTime = 0;
    	
    	public DeskReport(int deskNo, int servedCustomerCount, int soldItemCount, int spentTime){
    		this.deskNo = deskNo;
    		this.servedCustomerCount = servedCustomerCount;
    		this.soldItemCount = soldItemCount;
    		this.spentTime = spentTime;
    	}
    	
    	public int getDeskNo() {
    		return deskNo;
    	}
    
    	public int getServedCustomerCount() {
    		return servedCustomerCount;
    	}
    
    	public int getSoldItemCount() {
    		return soldItemCount;
    	}
    
    	public int getSpentTime() {
    		return spentTime;
    	}
    	
    }
    
    public class Customer{
    	private int itemCount = 0;
    	
    	public Customer(int itemCount){
    		this.itemCount = itemCount;
    	}
    	
    	public int getItemCount(){
    		return this.itemCount;
    	}
    }

    Симуляция примитивнейшая, демонстрирует скорее работу планировщика потоков, нежели магазина. Но если ее развить: смоделировать неравномерное "поступление" покупателей в магазин, очередь к кассам, случайный выбор кассы покупателем и т.д., можно узнать много интересного о работе магазинов. А пока, чтоб не было совсем уж бессмысленно, считается хоть какая-то примитивная статистика.
    Ответ написан
    Комментировать
  • Как понять принцип работы даной схемы?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Вот книжка, глава 4 пункт 1. Если совсем лень читать и разбираться, читайте краткое описание на сайте, откуда выдернули схему. Ну, а для совсем ленивых и не умеющих искать информацию в интернет вот:
    e60e16a33748443e8d425965235ad10c.jpg8ec420171b97480e8118c6c85bfb2fd8.jpg
    P.S. Труд проделан исключительно в знак уважения к светлой памяти "Котяр" (транзисторов КТ315/361).
    Ответ написан
    Комментировать
  • Нормализация БД. Зло или добро?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Нормализация ради нормализации - зло в подавляющем большинстве случаев! Нормализация, как часть продуманного проектирования или рефакторинга модели данных - однозначно добро. Структура БД должна быть, по возможности, оптимальной для запросов, выполняемых на ней приложением, а вовсе не соответствовать какой-то там нормальной форме :) Это "оптимально" может в каждом конкретном случае зависеть от кучи факторов, начиная от самих запросов и особенностей конкретной использованной СУБД, и вплоть до объемов доступной памяти и скорости обращения к диску.
    Ответ написан
    Комментировать
  • Как справиться с перекручиванием кабелей на многоантенной непрерывно вращающейся установке?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Поместить всю электронику (в т.ч. ВЧ) на вращающуюся часть. Снаружи, со статичной части, подавать туда только питание, например, индукционно или через скользящие контакты. Если туда нужно подавать ещё какой-то полезный (например, управляющий) сигнал или снимать оттуда какие-то данные, делать это также по радиоканалу или оптически (ИК или лазер). Посоветовать конкретнее можно только зная конкретный кейс: частоты, полоса пропускания канала данных, мощность и т.д.
    Ответ написан
    2 комментария
  • Что внутри панельной антены (MIMO LTE)?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Там внутри скорее всего т.н. patch antenna... определенный профиль, вытравленный на обычном стеклотекстолите (FR4), в редких случаях, обычно, для высоких частот, на другом субстрате, типа керамики. В определенной точке просверлена дырка, к которой припаяна центральная жила коаксиального кабеля.

    UPD:
    Выглядеть это может, например, вот так:

    c75d5c88f89f4f84adeed285292a8729.jpg

    Ну, а на тему "взять и скопировать"... как уже сказали, результат будет зависеть от очень большого количества факторов - от точности копирования и диэлектрических параметров использованных материалов, и вплоть до толщины металлизации и цвета лака, и даже небольшое отклонение может легко свести эффективность антенны в лучшем случае к эффективности простого куска провода. Так что, без знаний теории рассчета антенн и соотв. дорогого измерительного оборудования для окончательной настройки затея выглядит весьма сомнительной :)
    Ответ написан
    6 комментариев
  • Как получить пароль активного соеденения WIFI используя C#?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    XP / 2003
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WZCSVC\Parameters\Interfaces\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}

    Vista / 7 / 8
    C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\{Random-GUID}.xml

    где ххх - GUID соотв. интерфейса.

    Однако, они хранятся там в зашифрованом виде, и для расшифровки нужно воспользоваться соотв. функциями Windows Cryptography, в частности CryptUnprotectData. Однако, (чтоб жизнь не казалась малиной!) сделать это можно только в системном контексте, так что, код придется инжектить через NtCreateThreadEx или выполнять в установленном системном сервисе.

    Учитывая все эти подскоки, не думаю, что имеет смысл детально разбирать, как это все сделать, ибо, если Ваш уровень понимания это позволяет, приведенных данных достаточно для самостоятельного движения в нужном направлении, а если нет, то это все равно не поможет. Тогда лучше воспользоваться готовыми рабочими тулзами, которых на просторах Инета тонны.
    Ответ написан
    1 комментарий
  • Когда набор связанных данных можно считать базой данных?

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

    Например, в контексте поиска телефонных номеров...
    Городской телефонный справочник - это база данных, т.к. данные в нем связаны таким образом, что позволяют находить номера. Если же мы перекроим его так, что каждому телефонному номеру будет поставлен в соответствие номер страницы, на которой этот номер напечатан, а каждой фамили - сумма отдельных цифр в телефонном номере, этот набор данных перестанет быть базой данных в контексте заявленной области (хотя в нем будут все данные из исходного набора, и даже больше)... зато легко может стать таковой для каких-нибудь гороскопно-нумерологических задач.
    Ответ написан
    3 комментария
  • Есть ли в java стандартные инструменты для написания программ на чистом bytecode?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Комментировать
  • Что такое замещение в ООП?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Полиморфизм - один из трех фундаментальных принципов ООП-парадигмы.

    Замещение (также "перекрытие" или "переопределение" методов) - общее название механизма, предоставляемого ЯП для поддержки применения этого принципа при проектировании иерархий классов.

    Конкретные детали реализации механизма могут отличаться, (как, например в Java и JS), но суть с т.з. ООП остается той же. А вот в С, например, такого механизма вообще нет, однако это не препятствует (просто не помогает!) писать объектно ориентированный код на этом языке.

    Так что, если под "замещением" понимается именно overriding, я бы не стал так широко отождествлять цель и средство ее достижения :)
    Ответ написан
    Комментировать
  • Почему полиморфизм так работает?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Объяснить суть полиморфизма "на пальцах" довольно просто. Представьте себе класс Телефон. Его спроектировал некто в далеких 80-х, и определил в нем метод набратьНомер(). А потом другой программист в 90-х отнаследовал от него класс МобильныйТелефон и перекрыл метод набратьНомер(), т.к. грубо говоря, в новом устройстве набор производится уже не импульсно, а тонально. А потом третий программист отнаследовал от него класс Смартфон. При этом он не стал трогать метод набратьНомер(), а просто добавил методы для нового функционала, типа определитьПоложениеПоGPS() и т.д.

    Теперь представьте себе пользователя. Он родом из 80-х и понятия не имеет о тональном наборе и GPS... но если ему в руки дать любое из этих устройств, он сможет набрать номер и сделать звонок. Почему? Потому, что он умеет использовать метод набратьНомер(), и большего ему знать не нужно.

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

    Применительно к ООП, пользователь - это переменная, содержащая ссылку на экземпляр класса. Ее тип (как она была объявлена) - это "набор знаний" о возможностях этого экземпляра. И т.к. Смартфон в своей основе является Телефон-ом, мы вполне можем дать его в руки гипотетическому пользователю из 80-х:
    Телефон устройство = new Смартфон();
    в результате чего он сможет сделать звонок:
    устройство.набратьНомер("03");
    А вот определить свое местоположение он не сможет, пока не узнает о существовании соотв. метода:
    Смартфон усовершенствованноеУстройство = (Смартфон)устройство;

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

    P.S. Кстати, в этом примере мы затронули не только полиморфизм, но и наследование, и инкапсуляцию (пользователя "снаружи" совершенно не интересует, как именно производится набор номера - тонально, импульсно или еще как-то иначе)... так сказать, все три кита ООП в одном флаконе. И только так вообще имеет смысл рассматривать эти принципы, т.к. они по сути неотделимы друг от друга, как Отец, Сын и Святой Дух в Христианстве или же длина, ширина и высота в трехмерном пространстве :) Если это понять, ООП становится совершенно простой и естественной парадигмой программирования.
    Ответ написан
    8 комментариев
  • Документация к SOAP API. Нужна ли она и справедливо ли возражение о том, что WSDL достаточно?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Послать такого менеджера нужно на... курсы по WSDL, в частности, чтоб про "wsdl:documentation" узнал и устыдился. А вообще, типы параметров, определенные WSDL - это, как бы, одно, а вот их предназначение - совсем другое. Уважающие себя разрабы ко второму не только пишут доку в WSDL, но могут даже нарисовать UML и написать текст с примерами, если логика интерфейса требует этого ввиду своей нетривиальности.

    P.S. Впрочем, самоуважение, как и профессионализм, не относятся к ключевым скилам среднестатистического современного менеджера :)
    Ответ написан
    Комментировать
  • Можно на пальцах объяснить, про суть localhost и порты?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Если человек, не знакомый с арифметикой захочет понять, что означает "два плюс три равно пять", ему недостаточно объяснить, что такое "два", или что такое "плюс"! Так же и тут. Для понимания, что такое localhost, сначала нужно понять, что такое хост вообще, что такое сеть, что такое имя хоста, что такое сервер, как это все связано с адресами и номерами портов, а те - с, собственно, одним конкретным компом (который, в зависимости от контекста, могут называть и "хостом", и "сервером", и "localhost"). Так что, на всякий случай:

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

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

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

    На одном компе (=хосте, сервере) может одновременно выполняться несколько разных программ-серверов. Для того, чтоб обратиться к конкретной из них (адрес-то у всех один и тот же!), в протоколе TCP/IP используются разные номера портов.

    Если на компе запущен, например, HTTP сервер (= Webserver, например, Nginx или Apache), он "слушает" порт 80, а если не запущен, порт 80 никто не слушает, и если обратиться к такому хосту (= серверу, компу) по его адресу в порт 80, никакого ответа не придет... хотя сам хост и будет доступен.

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

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

    localhost - "общеизвестное" имя компа для самого себя и ему соответствует IP адрес 127.0.0.1. Это - общепринятая договоренность, которую просто нужно знать. Если говорят "установить сервер на localhost", это означает "установить на тот самый комп, с которого и обращаться к этому серверу".
    Ответ написан
    1 комментарий