• 0.34

  • +0.54

Мой Чат.

Всем привет!
Хочу поделиться своим небольшим проектом:
Взял одну из больших задач (Чат) и изменил, обновил, добавил много нового и вот что из этого вышло:



Что изменено/добавлено по сравнению с оригинальной задачей:

— Конфигурацию читает из файла properties при помощи класса ResourceManager
— Добавлены чат румы и возможность клиенту создавать свои чаты
— Добавлены приватные сообщения
— Полностью новый swing интерфейс клиента
— Добавлены горячие клавиши для отправления сообщений и команд клиента
— Добавлено окно настроек
— Добавлены смайлики
— Сделаны вкладки для отображения сообщений из разных чат румов и приватных чатов
— Добавлен счетчик непрочитанных сообщений
— Сделано дерево пользователей для отображения пользователей в чатах и вызова приватных сообщений
— Бот научился отвечать смайликом на смайлик
— Добавил логгер log4j
— Добавил добавил тесты jUnit
— Завернул все это в мавен и разбил на модули

ссылка на гит хаб: github.com/DevCorvette/chat

Кому интересно, заходите, смотрите, комментируйте, буду рад :)
Конструктивной критике тоже буду рад.

P.S.: Тот кто еще не решал задачу — не ищите у меня готовых решений, там практически все переписано ;)

task30.task3008 Чат (14)

Ребята, помогите разобраться с задачей:

Чат (14)
Приступим к написанию главного функционала класса Client.

1. Добавь метод void run(). Он должен создавать вспомогательный поток SocketThread, ожидать пока тот установит соединение с сервером, а после этого в цикле считывать сообщения с консоли и отправлять их серверу. Условием выхода из цикла будет отключение клиента или ввод пользователем команды ‘exit‘.

Для информирования главного потока, что соединение установлено во

вспомогательном потоке, используй методы wait и notify объекта класса Client.

Реализация метода run должна:

а) Создавать новый сокетный поток с помощью метода getSocketThread.

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

в) Запустить вспомогательный поток.

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

д) После того, как поток дождался нотификации, проверь значение clientConnected. Если оно true – выведи «Соединение установлено. Для выхода наберите команду ‘exit’.«. Если оно false – выведи «Произошла ошибка во время работы клиента.».

е) Считывай сообщения с консоли пока клиент подключен. Если будет введена команда ‘exit‘, то выйди из цикла.

ж) После каждого считывания, если метод shouldSendTextFromConsole() возвращает true, отправь считанный текст с помощью метода sendTextMessage().

2. Добавь метод main(). Он должен создавать новый объект типа Client и вызывать у него метод run().

package com.javarush.task.task30.task3008.client;

import com.javarush.task.task30.task3008.Connection;
import com.javarush.task.task30.task3008.ConsoleHelper;
import com.javarush.task.task30.task3008.Message;
import com.javarush.task.task30.task3008.MessageType;

import java.io.IOException;

/**
 * Класс для клиента
 */
public class Client {
    protected Connection connection;
    private volatile boolean clientConnected = false;

    /**
     * Метод запрашивает ввод адреса сервера у пользователя и вернуть введенное значение.
     */
    protected String getServerAddress(){
        ConsoleHelper.writeMessage("Введите адрес сервера");
        return ConsoleHelper.readString();
    }

    /**
     * Метод запрашивает ввод порта сервера и возвращать его.
     */
    protected int getServerPort(){
        ConsoleHelper.writeMessage("Введите порт сервера");
        return ConsoleHelper.readInt();
    }

    /**
     * Метод запрашивает имя пользователя и возвращает его
     */
    protected String getUserName(){
        ConsoleHelper.writeMessage("Введите имя пользователя");
        return ConsoleHelper.readString();
    }

    /**
     * В данной реализации клиента всегда возвращает true
     */
    protected boolean shouldSendTextFromConsole(){
        return true;
    }

    /**
     * Возвращает экземпляр SocketThread
     */
    protected SocketThread getSocketThread(){
        return new SocketThread();
    }

    /**
     * Метод создает новое текстовое сообщение, используя переданный текст и
     * отправляет его серверу через соединение connection
     */
    protected void sendTextMessage(String text){
        try {
            connection.send(new Message(MessageType.TEXT, text));
        } catch (IOException e) {
            ConsoleHelper.writeMessage("Ошибка во время отправки сообщения");
            clientConnected = false;
        }
    }

    /**
     *
     */
    public synchronized void run(){
        try {
            SocketThread socketThread = getSocketThread();
            socketThread.setDaemon(true);
            socketThread.run();
            this.wait();

            if (clientConnected){
                ConsoleHelper.writeMessage("Соединение установлено. Для выхода наберите команду ‘exit’.");
            }
            else ConsoleHelper.writeMessage("Произошла ошибка во время работы клиента.");

            while (clientConnected){
                String s = ConsoleHelper.readString();
                if (!s.equals("exit")){
                    if (shouldSendTextFromConsole())
                        sendTextMessage(s);
                }
                else break;
            }
        } catch (InterruptedException e) {
            ConsoleHelper.writeMessage("Произошла ошибка");
        }
    }

    /**
     * Класс отвечает за поток, устанавливающий сокетное соединение и читающий сообщения сервера
     */
    public class SocketThread extends Thread {
        @Override
        public void run() {

        }
    }

    public static void main(String[] args) {
        new Client().run();
    }
}

Дальше начинаются глюки:
если поле connection не инициализировать, как в коде выше, то валидатор пишет: Программа работала слишком долго и была закрыта!
Если поле инициализировать в конструкторе, то проблема time out исчезает, но о том что его нужно инициализировать ни где не написано. Наверняка косяк условия задачи.
Если поле инициализировать то валидатор вроде как ругается на пункт: Если метод shouldSentTextFromConsole возвращает true, должен быть вызван метод sendTextMessage со считанным текстом в качестве параметра.
Но с этим у меня вроде все ок:
while (clientConnected){
                String s = ConsoleHelper.readString();
                if (!s.equals("exit")){
                    if (shouldSendTextFromConsole())
                        sendTextMessage(s);
                }
                else break;
            }

В общем очень прошу помощи, кто уже решал, подскажите, в чем косяк?

Зачем нужен Singleton?

Решаю сейчас большую задачу 27 уровня. Там нас просят создать класс StatisticManager и сделать его синглтоном. Ок без проблем! В дальнейшем, в других классах программы мне приходится вызывать методы StatistcManager'а, и тут я начинаю сталкиваться с небольшими раздражающими моментами, приходится писать ссылочную переменную
StatisticManager statisticManager = StatisticManager.getInstance();
statisticManager.method();

или так:
StatisticManager.getInstance().method();

Это же столько лишних букв! Намного проще было бы сделать класс статическим и писать так:
StatisticManager.method();

Думаю, наверно синглтон в чем то выигрывает перед статическим классом? Читаю статью на Хабре и только усиливаю свои сомнения. Синглтон как минимум в данной задаче — избыточен.
Пока для себя вижу только одно применение синглтона — если нам нужно сэкономить ресурсы и создать объект не сразу, а когда — нибудь потом, когда он нам понадобиться. В остальных случаях статический класс — самое оно.
На хабре еще что-то пишут про наследование, но слабо пока могу себе представить такую ситуацию, наверно тоже не очень часто.
Если я не прав, поправьте меня, пожалуйста. Просто хочу лучше понять принципы проектирования и возможно это поможет разобраться кому то еще :)

level28.lesson15.big01 задание 2

Нужна помощь!
Первый раз у меня такой тупняк с таким простым заданием.

Задание 2

1. Создай класс Controller, в нем будет содержаться бизнес логика.

2. В Controller добавь паблик конструктор, который будет принимать столько провайдеров,
сколько в него передадут для обработки.
Сохрани их в приватное поле providers.
Помнишь, как это делать? Нужно нажать на аргументе конструктора Alt+Enter,
выбрать Create Field for Parameter 'providers'

3. Если провайдеры не переданы в конструктор контроллера, то брось IllegalArgumentException.

4. Создай метод toString в классе Controller (Alt+Insert -> toString())
со стандартной реализацией (должен выводить поле providers)

5. В методе main создай провайдер, а потом создай контроллер с этим провайдером.

6. В методе main выведи в консоль созданный экземпляр Controller-а.

public class Controller {
    private Provider[] providers;

    @SafeVarargs
    public Controller(Provider... providers) {
        if (providers == null || providers.length == 0) throw new IllegalArgumentException();
        this.providers = providers;
    }

    @Override
    public String toString() {
        String s = Arrays.toString(providers);
        return s.substring(1, s.length() - 1);
    }
}


public class Aggregator {
    public static void main(String[] args) {
        Provider provider = new Provider(new Strategy() {
        });
        Controller controller = new Controller(provider);
        System.out.println(controller);
    }
}


Кто решил, подскажите в чем тут тонкость реализации?
toString пробовал писать по разному.

BufferedReader vs Scanner

Решал сегодня задачу level22.lesson09.task01. В условии нужно считать слова разделенные пробелом, состоящие из несколько строк. Смотря комментарии к этой задачи удивился, что люди считывают строки в основном через BufferedReader. При этом у них возникает куча проблем, строку нужно разделять на слова при помощи split() с переносом строки тоже возникают трудности. Подобные вопросы возникали у меня и на более ранних задачах. В связи с этим вопрос: почему у ДжаваРашевцев такая не любовь к Scanner`у? Это всеобщее заблуждение или я чего-то не знаю?
Ведь Scanner отлично справляется, если нам нужно считать не строку целиком, а именно слова разделенные пробелами и на разных строках. Не надо вставлять ни каких костылей в виде сплитов и думать о переносе строк.