• Как найти связанные элементы с разных таблиц?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    FIND_IN_SET(`attachments`.`id`, `wall`.`attachments`) > 0

    Но по уму надо приводить базу в нормальную форму, делать отдельную таблицу связи.
    Ответ написан
    5 комментариев
  • Есть такая функция замены id в view андроид студия?

    zagayevskiy
    @zagayevskiy Куратор тега Java
    Android developer at Yandex
    Если список, значит используй RecyclerView. Будет список данных, которые надо отобразить. В этом списке будут айдишники(но не айдишник вьюхи или другого ресурса, а айдишник данных. Например, порядковый номер). По ним и ориентируйся.
    Ответ написан
    Комментировать
  • Проверить существование слова в словаре?

    @zhaar
    Возьми словарь побольше, возьми любую БД и ищи слова запросом по like '%w%r%d'.
    Оффлайн решение с минимум кодинга
    Ответ написан
    3 комментария
  • Android development: каким должен быть масштабируемый код?

    @terminator-light
    Для того чтобы писать масштабируемый необходимо знание не только паттернов GOF,
    но и архитектурных: MVC, MVP, MVVM, MVI. Важно изучить достоинства и недостатки каждого.
    Во главу угла также стоят принципы SOLID, далее приведу небольшие примеры:
    1. Single Responsibility principle - принцип единственной ответственности. Метод/класс должен выполнять только одну задачу. Например, метод, предназначенный для загрузки данных из сети, не должен заниматься обработкой ошибок.
    Псевдокодом напишу:
    spoiler
    public void loadData(String url){
    	repository.fetchProducts().get(products->{
    		view.showProducts(products);
    	}, throwable ->{
    		if(throwable instanceof IOexception){
    			view.showNoNetwork();
    		}else(throwable instanceof HTTPException){
    			HTTPException exception = (HTTPException)throwable;
    		 	switch(exception.getCode()){
    		 		case 400:
    		 			view.showError(exception.getMessage());
    		 			break;
    		 		case 401:
    		 			view.showUnauthorized();
    		 			break;
    		 			...
    		 	}
    		}
    		...
    	});
    }

    Вместо этого вторую часть нужно выделить в другой метод/класс.
    spoiler
    public void loadData(String url){
    	repository.fetchProducts().get(products-> view.showProducts(products), 
    		throwable -> ErrorUtil.handleError(throwable, view));
    }
    
    public class ErrorUtil{
    	public static void handleError(Throwable throwable, View view){
    		if(throwable instanceof IOexception){
    			view.showNoNetwork();
    		}else(throwable instanceof HTTPException){
    			HTTPException exception = (HTTPException)throwable;
    		 	switch(exception.getCode()){
    		 		case 400:
    		 			view.showError(exception.getMessage());
    		 			break;
    		 		case 401:
    		 			view.showUnauthorized();
    		 			break;
    		 			...
    		 	}
    		}
    		...
    	}
    }


    2. Open/Closed principle - принцип открытости/закрытости. Код должен быть открыт для добавления функциональности, но закрыт для изменения.
    Например, есть такой код для работы с тулбаром. Если экранов будет много с разными тулбарами,
    то постоянно придется добавлять новую ветку case, а значит изменять класс ToolbarManager,
    при этом есть возможность появления ошибки в местах, касающихся и других case-веток
    spoiler
    public class ToolbarManager{
    	public void showToolbar(int type){
    		switch(type){
    			case MAIN:
    				....
    				//огромный кусок кода для показа тулбара для главного экрана
    				....
    				break;
    			case PROFILE:
    				...
    				//огромный кусок кода для показа тулбара для экрана профиля
    				...
    			...
    		}
    	}
    }

    Решение: воспользоваться одним из принципов ООП - полиморфизмом. Теперь, если понадобится добавить
    новый экран, нужно будет просто реализовать интерфейс, и это не будет касаться кода других экранов.
    spoiler
    public interface ToolbarManager{
    	void showToolbar();
    }
    
    public class MainToolbarManager implements ToolbarManager{
    	public void showToolbar(){
    		....
    		//огромный кусок кода для показа тулбара для главного экрана
    		....
    	}
    }
    
    public class ProfileToolbarManager implements ToolbarManager{
    	public void showToolbar(){
    		....
    		//огромный кусок кода для показа тулбара для экрана профиля
    		....
    	}
    }

    3. Liskov Substitution principle - принцип подстановки Барбары Лисков гласит: Если класс B - это подтип A, то мы должны иметь
    возможность заменить A на B, не нарушая поведение программы.
    Для данного принципа не могу придумать пример, связанный с Android, но мне понравился этот пример,
    взятый из этого сайта https://www.baeldung.com/solid-principles
    spoiler
    public interface Car {
        void turnOnEngine(); //запустить двигатель
        void accelerate(); //подать газ
    }
    
    public class MotorCar implements Car {
     
        private Engine engine;
     
        public void turnOnEngine() {
            //вруби мотор!
            engine.on();
        }
     
        public void accelerate() {
            //поезжай вперед!
            engine.powerOn(1000);
        }
    }

    Наш класс удовлетворяет интерфейсу, у нас есть машина, которая имеет свой мотор,
    и мы можем ускориться. Но мы живем в 2019 году, а Илон Маск старательный человек. Мы живем в эпоху электрокаров:
    spoiler
    public class ElectricCar implements Car {
     
        public void turnOnEngine() {
            throw new AssertionError("А у меня вообще нет двигателя");
        }
     
        public void accelerate() {
            //ускорение сумасшедшее!
        }
    }

    Выбрасывая машину без двигателя в общую кучу, мы меняем поведение нашей программы.
    Это грубое нарушение принципа подстановки Барбары Лисков. Решить данную проблему будет непросто.
    Но одним из решений было бы разбиение нашей модели на мелкие интерфейсы, которые учитывают состояние без двигателя
    spoiler
    public interface Engineful {
    
        void turnOnEngine();
    }
    public interface Acceleratable {
        void accelerate();
    }
    public class MotorCar implements Engineful, Acceleratable {
     
        private Engine engine;
     
        public void turnOnEngine() {
            //вруби мотор!
            engine.on();
        }
     
        public void accelerate() {
            //поезжай вперед!
            engine.powerOn(1000);
        }
    }
    public class ElectricCar implements Acceleratable {
        public void accelerate() {
            //ускорение сумасшедшее!
        }
    }


    4. Interface segregation principle - прин­цип раз­де­ле­ния интер­фейса.
    Создавайте гранённые мелкие интерфейсы. Клиентский код не должен зависеть от функций интерфейса, которые он
    не будет использовать. Возьмем пример из Android SDK, убрал некоторые детали для простоты:
    spoiler
    public interface TextWatcher{
        public void beforeTextChanged(CharSequence s, int start, int count, int after);
        public void onTextChanged(CharSequence s, int start, int before, int count);
        public void afterTextChanged(Editable s);
    }


    И клиентский код:
    spoiler
    editText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
        }
    
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
    
        }
    
        @Override
        public void afterTextChanged(Editable s) {
        	//метод, который нам нужен
        	//какие-то полезные действия
        }
    });

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

    5. Dependency inversion principle - принцип инверсии зависимостей.
    Высокоуровневые модули не должны зависеть от низкоуровневых.
    Абстракции не должны зависеть от деталей. Но детали зависят от абстракций.
    Пример: у нас есть класс Repository, отвечающий за получение данных из разных источников.
    Проблема в том, что объекты жестко заданы в конструкторе. И мы не имеем возможности
    поменять реализацию AppDataBase на FakeDataBase для тестов.
    spoiler
    public class Repository{
    	private final NetworkManager networkManager;
    	private final AppDataBase appDataBase;
    	public Repository(){
    		this.networkManager = new NetworkManager();
    		this.AppDataBase = new AppDataBase();
    	}
    }


    Поэтому нам следует как-то развязать жесткую связь, выделив интерфейсы.
    spoiler
    public interface RemoteDataSource{}
    public interface LocalDataSource{}
    public class NetworkManager implements RemoteDataSource{}
    public class AppDataBase implements LocalDataSource{}
    
    public class Repository{
    	private final RemoteDataSource remoteDataSource;
    	private final LocalDataSource localDataSource;
    	public Repository(RemoteDataSource remoteDataSource, LocalDataSource localDataSource){
    		this.remoteDataSource = remoteDataSource;
    		this.localDataSource = localDataSource;
    	}
    }


    А теперь мы можем тестовую реализацию источников:
    public class FakeNetworkManager implements RemoteDataSource{}
    public class FakeAppDataBase implements LocalDataSource{}

    и вызов:
    Repository repository = new Repository(new FakeNetworkManager(), new FakeAppDataBase());


    У Мартина Фаулера есть хорошая книга Рефакторинг: Улучшение существующего кода и Р.Мартина Чистый код
    Ответ написан
    2 комментария